本文来自SynchronizationContext可能与ExecutionContext以下情况有关的州:
Run Code Online (Sandbox Code Playgroud)private void button1_Click(object sender, EventArgs e) { button1.Text = await Task.Run(async delegate { string data = await DownloadAsync(); return Compute(data); }); }这是我的心理模型告诉我将使用此代码发生的事情.用户单击button1,导致UI框架在UI线程上调用button1_Click.然后代码启动一个工作项以在ThreadPool上运行(通过Task.Run).该工作项启动一些下载工作并异步等待它完成.然后,ThreadPool上的后续工作项对该下载的结果执行一些计算密集型操作,并返回结果,从而导致在UI线程上等待的任务完成.此时,UI线程处理此button1_Click方法的其余部分,将计算结果存储到button1的Text属性中.
如果SynchronizationContext不作为ExecutionContext的一部分流动,我的期望是有效的.然而,如果它确实流动,我将非常失望.Task.Run在调用时捕获ExecutionContext,并使用它来运行传递给它的委托.这意味着调用Task.Run时当前的UI SynchronizationContext将流入Task,并在调用DownloadAsync并等待生成的任务时为Current.这意味着await将看到Current SynchronizationContext并将异步方法的剩余部分作为在UI线程上运行的延续.这意味着我的Compute方法很可能会在UI线程上运行,而不是在ThreadPool上运行,从而导致我的应用程序出现响应问题.
故事现在变得有点混乱:ExecutionContext实际上有两个Capture方法,但只有一个是公共的.内部的(mscorlib内部)是mscorlib公开的大多数异步功能所使用的,它可选地允许调用者禁止捕获SynchronizationContext作为ExecutionContext的一部分; 对应于此,还有一个Run方法的内部重载,它支持忽略存储在ExecutionContext中的SynchronizationContext,实际上假装没有捕获一个(这也是mscorlib中大多数功能使用的重载).这意味着几乎任何其核心实现驻留在mscorlib中的异步操作都不会将SynchronizationContext作为ExecutionContext的一部分流,但是其核心实现驻留在任何其他位置的任何异步操作都会将SynchronizationContext作为ExecutionContext的一部分进行流动.我之前提到异步方法的"构建器"是负责在异步方法中流动ExecutionContext的类型,并且这些构建器确实存在于mscorlib中,并且它们确实使用内部重载...因此,SynchronizationContext不作为ExecutionContext的一部分流动等待(再次,这与任务等待者如何支持捕获SynchronizationContext和Post'ing回到它是分开的).为了帮助处理ExecutionContext确实流动SynchronizationContext的情况,异步方法基础结构尝试忽略由于流动而设置为Current的SynchronizationContexts.
然而,它是不完全清楚,我在这件事会发生.看来,当公众会发生ExecutionContext.Capture使用方法和内部Task.Run抑制流动过载SynchronizationContext与ExecutionContext时不使用,但我不知道什么时候,这将是.
在我的.NET 4.5的测试Task.Run似乎并不流动SynchronizationContext与ExecutionContext:
private async void button1_Click(object sender, EventArgs e) {
Console.WriteLine("Click context:" + SynchronizationContext.Current);
button1.Text = await Task.Run(async delegate {
// In my tests this always returns …Run Code Online (Sandbox Code Playgroud) 我有一个大型CSV文件的解析器类.解析方法逐行读取大文件的工作是在backgroundWorker中完成的.使用该backgroundWorker.ReportProgress方法将完整信息的百分比传递给UI线程,以便表单上的进度条可以完成其任务.这工作正常.
但是,我还想提出一个自定义事件,该事件将从CSV文件第一行获取的字段名列表发送回UI(WPF),以便将它们放在下拉列表中.如果解析器遇到格式错误的线路或其他路障,我还想通过事件通知用户.
我的解析器进程在后台执行只能引发一个事件吗?或者必须将SynchronizationContext.Current从主UI线程传递到我的解析器类,然后使用Post方法?
public static void Init()
{
//var task = GetSource1();
//var task = GetSource2();
//var task = GetSource3();
var task = MyClient();
MessageBox.Show(task.Result);
}
private static async Task<string> GetSource1()
{
var sourceTask = WebClient();
sourceTask.ConfigureAwait(false);
return await sourceTask;
}
private static async Task<string> GetSource2()
{
var sourceTask = MyClient();
sourceTask.ConfigureAwait(true);
return await sourceTask;
}
private static async Task<string> GetSource3()
{
var sourceTask = MyClient();
sourceTask.ConfigureAwait(false);
return await sourceTask;
}
private static async Task<string> WebClient()
{
return await new WebClient().DownloadStringTaskAsync("http://4pda.ru").ConfigureAwait(false);
}
private …Run Code Online (Sandbox Code Playgroud) .net c# synchronizationcontext task-parallel-library async-await
下面的代码块仅对 Npgsql 导致跨线程无效操作异常(不包括 sqlclient、sqlite、mysql、文件读取异步)。
private async void button1_Click(object sender, EventArgs e)
{
var strBuilder = new Npgsql.NpgsqlConnectionStringBuilder()
{
Host = "localhost",
Username = "postgres",
Password = "password"
};
using (var conn = new Npgsql.NpgsqlConnection(strBuilder.ConnectionString))
{
try
{
await conn.OpenAsync();
if (conn.State ==ConnectionState.Open)
{
MessageBox.Show("Connected");
this.button1.Text = "CROSS-THREAD-With-NPGSQL";
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
我查看了 Npgsql 的代码并找到了此链接: https://github.com/npgsql/npgsql/blob/2dd46e7c544caf3302ca7b89dd888a16dccf5c2c/src/Npgsql/PGUtil.cs
文件底部写道:
该机制用于在执行 Npgsql 代码时临时将当前同步上下文设置为 null,从而使所有等待延续在线程池上执行。这取代了在任何地方放置ConfigureAwait(false)的需要,并且应该在所有表面异步方法中使用,无一例外。
我从 Roji(Npgsql 存储库的所有者)那里得到了相当多的解释,但我需要理解为什么我没有看到其他驱动程序的类似问题。npgsql 临时禁用 SynchronizationContext 的方式是否被认为是最佳实践?我正在尝试查看其他驱动程序的源代码,但这需要一段时间,所以我希望我能得到一些帮助,以朝着正确的方向前进。
编辑 1:Stephen Cleary 在下面给出了非常详细的答案,但我也想在这里发布我的一些发现。它可能会帮助其他人。2016 年 9 月 24 日,npgsql 用 NoSynchronizationContextScope 替换了所有的ConfigureAwait(false)。正如 Stephen 所解释的,NoSynchronizationContextScope …
c# database multithreading synchronizationcontext async-await
在wpf中,我有两个不同线程的窗口.
从窗口A'在线程中AI想要在窗口B'的Tread B中启动一个任务并等待线程A中的返回值.
它假设它可能但是如何?
你知道一个例子吗?