什么时候Task.Run流的SynchronizationContext与ExecutionContext中? [英] When does Task.Run flow SynchronizationContext with ExecutionContext?
问题描述
<一个href=\"http://blogs.msdn.com/b/pfxteam/archive/2012/06/15/executioncontext-vs-synchronizationcontext.aspx\"相对=nofollow>本文来自各国的 的SynchronizationContext
可与的ExecutionContext
流量:
私人无效的button1_Click(对象发件人,EventArgs的发送){
button1.Text =等待Task.Run(异步委托
{
字符串数据=等待DownloadAsync();
返回计算(数据);
});
}下面就是我的心智模型告诉我将与此code发生。一个
用户点击按钮1,导致UI框架调用的button1_Click
在UI线程。在code,则揭开序幕一个工作项上运行
线程池(通过Task.Run)。这项工作项开始一些下载工作
和异步等待它完成。随后的工作项目
在线程池,然后做的一些计算密集型操作
该下载的结果,并返回结果,引起工作即
正在等待UI线程来完成。在这一点,用户界面
线程处理这种方法的button1_Click的其余部分,存储
计算到按钮1的Text属性的结果。
我的期望是有效的,如果SynchronizationContext的不流动的一部分
的执行上下文。如果它流动,但是,我会缺阵
失望。 Task.Run捕获的ExecutionContext调用时,和
用它来运行传递给它的委托。这意味着,用户界面
SynchronizationContext的这是当前被调用时Task.Run
将流入任务和调用的同时将电流
DownloadAsync,等待所产生的任务。这则意味着,
中的await将看到当前的SynchronizationContext和邮政
异步方法的其余部分的延续,在跑回来
UI线程。这意味着我的计算方法则很可能是
在UI线程上运行,而不是在线程池,造成
响应问题,为我的应用程序。
这个故事现在变得有点混乱:ExecutionContext中实际上有两个捕获方法,但
只有其中之一是公开的。内部一(内部MSCORLIB)
使用通过从暴露的最异步功能之一
mscorlib程序,它可选择允许主叫方燮$ P $的PSS
的SynchronizationContext捕获的作为的ExecutionContext的一部分;
对应的是,这里还有运行的内部过载
支持忽略的一个存储方法的SynchronizationContext
在执行上下文,实际上pretending一人不捕获
(这是,再次在mscorlib中使用的大多数功能的过载)。
这意味着是pretty多的异步操作的
核心实现驻留在mscorlib中不会流
的SynchronizationContext作为的ExecutionContext的一部分,但任何
异步操作,其核心实现位于其他地方
将流动的SynchronizationContext作为的ExecutionContext的一部分。一世
previously提到,建设者的异步方法是在
负责异步方法流淌的ExecutionContext类型和
这些制造商就住在mscorlib程序,他们也使用内部
重载...因此,的SynchronizationContext不流动的部分
整个等待着(这又是怎么从单独的任务执行上下文
awaiters支持捕获的SynchronizationContext和Post'ing
回去吧)。为了帮助解决那里的ExecutionContext做的情况下,
流动SynchronizationContext的,异步方法的基础设施尝试
忽略由于被流设置为当前SynchronizationContexts。
块引用>然而,它是不完全清楚,我的在的这件事会发生。看来,当使用公共
ExecutionContext.Capture
法和内部Task.Run
重载燮它会发生流动presses的SynchronizationContext
与的ExecutionContext
终止的不的使用,但我不知道什么时候会。在我的.NET 4.5
Task.Run
测试似乎不流动的SynchronizationContext
与的ExecutionContext
:专用异步无效的button1_Click(对象发件人,EventArgs的发送){
Console.WriteLine(点击背景:+ SynchronizationContext.Current);
button1.Text =等待Task.Run(异步委托{ //在我的测试这个始终返回false
Console.WriteLine(的SynchronizationContext流到:+(SynchronizationContext.Current = NULL)!); 字符串数据=等待DownloadAsync();
返回计算(数据);
});
}所以我的问题是在什么情况下会
计算()
在UI方面(阻塞UI线程)的文章中讨论?运行解决方案
在做Task.Run流的SynchronizationContext与ExecutionContext中?
块引用>从不。
这文章的一点是,(对于公共API)流动
的ExecutionContext
将流向的SynchronizationContext
。但Task.Run
(和pretty多的异步操作,其核心执行驻留在mscorlib中)将永远不会做到这一点。开始,我的期望是,如果有效是假设的段落。他描述什么将的发生,如果
Task.Run
使用公共API的流动的ExecutionContext
。这的将会的原因问题如果的它这样做。这就是为什么它不会永远这样做。This article from states that
SynchronizationContext
may flow withExecutionContext
:private void button1_Click(object sender, EventArgs e) { button1.Text = await Task.Run(async delegate { string data = await DownloadAsync(); return Compute(data); }); }
Here’s what my mental model tells me will happen with this code. A user clicks button1, causing the UI framework to invoke button1_Click on the UI thread. The code then kicks off a work item to run on the ThreadPool (via Task.Run). That work item starts some download work and asynchronously waits for it to complete. A subsequent work item on the ThreadPool then does some compute-intensive operation on the result of that download, and returns the result, causing the Task that was being awaited on the UI thread to complete. At that point, the UI thread processes the remainder of this button1_Click method, storing the result of the computation into the button1’s Text property.
My expectation is valid if SynchronizationContext doesn’t flow as part of ExecutionContext. If it does flow, however, I will be sorely disappointed. Task.Run captures ExecutionContext when invoked, and uses it to run the delegate passed to it. That means that the UI SynchronizationContext which was current when Task.Run was invoked would flow into the Task and would be Current while invoking DownloadAsync and awaiting the resulting task. That then means that the await will see the Current SynchronizationContext and Post the remainder of asynchronous method as a continuation to run back on the UI thread. And that means my Compute method will very likely be running on the UI thread, not on the ThreadPool, causing responsiveness problems for my app.
The story now gets a bit messier: ExecutionContext actually has two Capture methods, but only one of them is public. The internal one (internal to mscorlib) is the one used by most asynchronous functionality exposed from mscorlib, and it optionally allows the caller to suppress the capturing of SynchronizationContext as part of ExecutionContext; corresponding to that, there’s also an internal overload of the Run method that supports ignoring a SynchronizationContext that’s stored in the ExecutionContext, in effect pretending one wasn’t captured (this is, again, the overload used by most functionality in mscorlib). What this means is that pretty much any asynchronous operation whose core implementation resides in mscorlib won’t flow SynchronizationContext as part of ExecutionContext, but any asynchronous operation whose core implementation resides anywhere else will flow SynchronizationContext as part of ExecutionContext. I previously mentioned that the "builders" for async methods were the types responsible for flowing ExecutionContext in async methods, and these builders do live in mscorlib, and they do use the internal overloads… as such, SynchronizationContext is not flowed as part of ExecutionContext across awaits (this, again, is separate from how task awaiters support capturing the SynchronizationContext and Post’ing back to it). To help deal with the cases where ExecutionContext does flow SynchronizationContext, the async method infrastructure tries to ignore SynchronizationContexts set as Current due to being flowed.
However it isn't exactly clear to me when this might happen. It appears that it will happen when the public
ExecutionContext.Capture
method is used and the internalTask.Run
overload that suppresses flowingSynchronizationContext
withExecutionContext
is not used, but I don't know when that would be.In my testing on .NET 4.5
Task.Run
does not seem to flow theSynchronizationContext
with theExecutionContext
: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); }); }
So my question is under what circumstances will
Compute()
be run on the UI context (blocking the UI thread) as discussed in the article?解决方案When does Task.Run flow SynchronizationContext with ExecutionContext?
Never.
The point of that article is that (the public API for) flowing
ExecutionContext
will flowSynchronizationContext
. ButTask.Run
(and "pretty much any asynchronous operation whose core implementation resides in mscorlib") will never do this.The paragraph starting with "My expectation is valid if" is hypothetical. He's describing what would happen if
Task.Run
use the public API for flowingExecutionContext
. This would cause problems if it did this. That's why it doesn't ever do this.这篇关于什么时候Task.Run流的SynchronizationContext与ExecutionContext中?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!