本文来自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)