【发布时间】:2013-10-23 17:25:55
【问题描述】:
This article 来自SynchronizationContext 可能与ExecutionContext 一起流动的状态:
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 线程上。然后代码启动一个工作项以在 线程池(通过 Task.Run)。该工作项开始一些下载工作 并异步等待它完成。后续工作项 然后在 ThreadPool 上执行一些计算密集型操作 该下载的结果,并返回结果,导致任务 正在等待 UI 线程完成。那个时候,UI 线程处理这个 button1_Click 方法的剩余部分,存储 计算结果到 button1 的 Text 属性中。
如果 SynchronizationContext 不作为一部分流动,我的期望是有效的 执行上下文。但是,如果它确实流动,我会非常痛苦 失望的。 Task.Run 在调用时捕获 ExecutionContext,并且 使用它来运行传递给它的委托。这意味着用户界面 调用 Task.Run 时当前的 SynchronizationContext 将流入任务并在调用时为当前 DownloadAsync 并等待生成的任务。这意味着 await 将看到 Current SynchronizationContext 并发布 异步方法的其余部分作为继续运行返回 用户界面线程。这意味着我的 Compute 方法很可能是 在 UI 线程上运行,而不是在 ThreadPool 上运行,导致 我的应用出现响应问题。
故事现在变得有点混乱:ExecutionContext 实际上有两个 Capture 方法,但是 其中只有一个是公开的。内部的(mscorlib 内部的) 是大多数异步功能使用的一种,从 mscorlib,它可以选择允许调用者抑制 捕获 SynchronizationContext 作为 ExecutionContext 的一部分; 与此相对应,还有一个内部的 Run 重载 支持忽略存储的 SynchronizationContext 的方法 在 ExecutionContext 中,实际上假装没有被捕获 (这也是 mscorlib 中大多数功能使用的重载)。 这意味着几乎任何异步操作 核心实现驻留在 mscorlib 中不会流动 SynchronizationContext 作为 ExecutionContext 的一部分,但任何 其核心实现驻留在其他任何地方的异步操作 将作为 ExecutionContext 的一部分流动 SynchronizationContext。一世 前面提到异步方法的“构建器”是 负责在异步方法中流动 ExecutionContext 的类型,以及 这些构建器确实存在于 mscorlib 中,并且确实使用了内部 重载……因此,SynchronizationContext 不会作为 跨等待的 ExecutionContext(这再次与任务的方式分开 等待者支持捕获 SynchronizationContext 和 Post'ing 回到它)。帮助处理 ExecutionContext 的情况 流 SynchronizationContext,异步方法基础结构尝试 由于正在流动而忽略设置为 Current 的 SynchronizationContexts。
但我不清楚何时这可能发生。当使用公共ExecutionContext.Capture 方法并且使用ExecutionContext 抑制流动SynchronizationContext 的内部Task.Run 重载时似乎会发生这种情况不使用,但我不知道什么时候就是这样。
在我对 .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);
});
}
所以我的问题是在什么情况下Compute() 会在文章中讨论的 UI 上下文(阻塞 UI 线程)上运行?
【问题讨论】:
标签: c# asynchronous async-await synchronizationcontext