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
重载会阻止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 false
Console.WriteLine("SynchronizationContext was flowed: " + (SynchronizationContext.Current != null));
string data = await DownloadAsync();
return Compute(data);
});
}
所以我的问题是在什么情况下{id}会在UI上下文中运行(阻止UI线程)?
答案 0 :(得分:7)
Task.Run何时将SynchronizationContext与ExecutionContext一起流动?
从不。
该文章的重点是(流动ExecutionContext
的公共API)将流SynchronizationContext
。但是Task.Run
(以及“几乎任何其核心实现驻留在mscorlib中的异步操作”)都不会这样做。
以“我的期望是有效的”开头的段落是假设的。他正在描述如果Task.Run
使用公共API流动ExecutionContext
,会会发生什么。如果 这样做,会导致问题。这就是为什么它永远不会这样做。