Task.Run什么时候使用ExecutionContext流动SynchronizationContext?

时间:2013-10-23 17:25:56

标签: c# asynchronous async-await synchronizationcontext

来自SynchronizationContext可能与ExecutionContext

一起流动的州的{p> This article
private void button1_Click(object sender, EventArgs e)  { 
    button1.Text = await Task.Run(async delegate 
    { 
        string data = await DownloadAsync(); 
        return Compute(data); 
    });  
}
     

这是我的心理模型告诉我将使用此代码发生的事情。一个   用户单击button1,导致UI框架调用button1_Click   在UI线程上。然后代码启动一个工作项来运行   ThreadPool(通过Task.Run)。该工作项开始一些下载工作   并异步等待它完成。后续工作项目   然后在ThreadPool上执行一些计算密集型操作   下载的结果,并返回结果,导致任务   正在等待UI线程完成。那时,用户界面   线程处理这个button1_Click方法的剩余部分,存储   计算结果到button1的Text属性。

     

如果SynchronizationContext不作为部分流动,我的期望是有效的   ExecutionContext。然而,如果它确实流动,我会非常痛苦   失望。 Task.Run在调用时捕获ExecutionContext,和   用它来运行传递给它的委托。这意味着UI   SynchronizationContext,当调用Task.Run时是当前的   将流入Task,并在调用时为Current   DownloadAsync并等待生成的任务。那意味着   await将看到Current SynchronizationContext并发布   其余的异步方法作为延续运行的   UI线程。这意味着我的计算方法很有可能   在UI线程上运行,而不是在ThreadPool上运行,导致   我的应用程序的响应能力问题。

     

故事现在变得有点混乱:ExecutionContext实际上有两个Capture方法,但是   其中只有一个是公开的。内部的(mscorlib内部)   是从大多数异步功能中使用的那个   mscorlib,它可以选择允许调用者抑制   捕获SynchronizationContext作为ExecutionContext的一部分;   对应于此,还有Run的内部过载   支持忽略存储的SynchronizationContext的方法   在ExecutionContext中,实际上假装没有捕获一个   (这也是mscorlib中大多数功能所使用的重载)。   这意味着几乎任何异步操作都是如此   核心实现驻留在mscorlib中不会流动   SynchronizationContext作为ExecutionContext的一部分,但是任何   异步操作,其核心实现位于其他任何位置   将SynchronizationContext作为ExecutionContext的一部分流动。一世   前面提到的异步方法的“构建者”是   负责在异步方法中流动ExecutionContext的类型,以及   这些建设者住在mscorlib,他们确实使用内部   重载......因此,SynchronizationContext不会作为其中的一部分流动   跨越等待的ExecutionContext(这再次与任务的方式分开)   awaiters支持捕获SynchronizationContext和Post'ing   回到它)。帮助处理ExecutionContext的情况   flow SynchronizationContext,异步方法基础结构尝试   忽略由于流动而设置为Current的SynchronizationContexts。

然而, 时可能会发生这种情况并不完全清楚。它似乎会在使用公开ExecutionContext.Capture方法时发生,并且内部Task.Run重载会阻止SynchronizationContextExecutionContext流动 ,但我不知道那会是什么时候。

在我对.NET 4.5的测试Task.Run似乎没有SynchronizationContextExecutionContext

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 false
        Console.WriteLine("SynchronizationContext was flowed: " + (SynchronizationContext.Current != null));

        string data = await DownloadAsync();
        return Compute(data);
    });
}

所以我的问题是在什么情况下{id}会在UI上下文中运行(阻止UI线程)?

1 个答案:

答案 0 :(得分:7)

  

Task.Run何时将SynchronizationContext与ExecutionContext一起流动?

从不。

该文章的重点是(流动ExecutionContext的公共API)将流SynchronizationContext。但是Task.Run(以及“几乎任何其核心实现驻留在mscorlib中的异步操作”)都不会这样做。

以“我的期望是有效的”开头的段落是假设的。他正在描述如果Task.Run使用公共API流动ExecutionContext会发生什么。如果 这样做,导致问题。这就是为什么它永远不会这样做。