await在异步操作后没有恢复上下文?

时间:2015-08-26 14:01:19

标签: c# .net async-await task-parallel-library

我已阅读Noseratio中的this question,其中显示了在等待完成其操作后TaskScheduler.Current不是相同的行为。

答案指出:

  

如果没有执行任何实际任务,则TaskScheduler.Current   与TaskScheduler.Default

相同

这是真的。我已经看到它了here

  
      
  • TaskScheduler.Default      
        
    • 返回ThreadPoolTaskScheduler
    • 的实例   
  •   
  • TaskScheduler.Current      
        
    • 如果从执行任务中调用将返回TaskScheduler   当前正在执行的任务
    •   
    • 如果从其他任何地方致电,将返回TaskScheduler.Default
    •   
  •   

然后我想,如果是这样,让我们​​创建一个实际的Task(而不仅仅是Task.Yield())并测试它:

async void button1_Click_1(object sender, EventArgs e)
{
    var ts = TaskScheduler.FromCurrentSynchronizationContext();
    await Task.Factory.StartNew(async () =>
    {
        MessageBox.Show((TaskScheduler.Current == ts).ToString()); //True

           await new WebClient().DownloadStringTaskAsync("http://www.google.com");

        MessageBox.Show((TaskScheduler.Current == ts).ToString());//False

    }, CancellationToken.None, TaskCreationOptions.None,ts).Unwrap();
}

First Messagebox为“True”,第二个为“False”

问题:

正如您所看到的,我确实创建了一个实际的任务。

我可以理解为什么第一个MessageBox会产生True。多数民众赞成:

  

如果从执行任务中调用将返回TaskScheduler   当前正在执行的任务

该任务的确有ts,即TaskScheduler.FromCurrentSynchronizationContext()

为什么上下文 对我来说,斯蒂芬的回答并不清楚。

其他信息:

如果我改写(第二个消息框):

MessageBox.Show((TaskScheduler.Current == TaskScheduler.Default).ToString());

确实会产生true。但为什么 ?

1 个答案:

答案 0 :(得分:10)

混淆的原因是:

  1. 用户界面没有“特殊”TaskScheduler。在UI线程上运行的代码的默认情况是TaskScheduler.Current存储ThreadPoolTaskSchedulerSynchronizationContext.Current存储WindowsFormsSynchronizationContext(或其他UI应用程序中的相关存储)
  2. ThreadPoolTaskScheduler中的
  3. TaskScheduler.Current并不一定意味着它是用于运行当前代码的TaskScheduler。它还意味着TaskSchdeuler.Current == TaskScheduler.Default,因此“没有使用TaskScheduler
  4. TaskScheduler.FromCurrentSynchronizationContext()未返回“acutal”TaskScheduler。它返回一个“代理”,可以将任务直接发布到捕获的SynchronizationContext
  5. 因此,如果您在开始任务之前(或在任何其他地方)运行测试,您将获得与等待之后相同的结果

    MessageBox.Show(TaskScheduler.Current == TaskScheduler.FromCurrentSynchronizationContext()); // False
    

    由于TaskScheduler.CurrentThreadPoolTaskSchedulerTaskScheduler.FromCurrentSynchronizationContext()返回SynchronizationContextTaskScheduler

    这是你的例子的流程:

    • 您可以从用户界面SynchronizationContextTaskScheduler(即SynchronizationContext)创建新的WindowsFormsSynchronizationContext
    • 使用Task.Factory.StartNew上的TaskScheduler计划您创建的任务。由于它只是一个“代理”,它将委托发布到WindowsFormsSynchronizationContext,并在UI线程上调用它。
    • 该方法的同步部分(第一次等待之前的部分)在UI线程上执行,同时与SynchronizationContextTaskScheduler相关联。
    • 该方法到达await并在捕获WindowsFormsSynchronizationContext时被“暂停”。
    • 如果在等待之后恢复继续,则会将其发布到WindowsFormsSynchronizationContext而不是SynchronizationContextTaskScheduler,因为SynchronizationContext具有优先权(this can be seen in Task.SetContinuationForAwait)。然后在UI线程上定期运行,没有任何“特殊”TaskScheduler所以TaskScheduler.Current == TaskScheduler.Default

    因此,创建的任务在代理TaskScheduler上运行,代理SynchronizationContext使用SynchronizationContext,但await后的延续发布到TaskScheduler而不是{{1}}。< / p>