什么时候Task.Run流的SynchronizationContext与ExecutionContext中? [英] When does Task.Run flow SynchronizationContext with ExecutionContext?

查看:161
本文介绍了什么时候Task.Run流的SynchronizationContext与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属性的结果。


  
  

我的期望是有效的,如果Sy​​nchronizationContext的不流动的一部分
  的执行上下文。如果它流动,但是,我会缺阵
  失望。 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 with ExecutionContext:

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 internal Task.Run overload that suppresses flowing SynchronizationContext with ExecutionContext 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 the SynchronizationContext with the 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);
    });
}

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 flow SynchronizationContext. But Task.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 flowing ExecutionContext. This would cause problems if it did this. That's why it doesn't ever do this.

这篇关于什么时候Task.Run流的SynchronizationContext与ExecutionContext中?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆