繁体   English   中英

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

[英]await does not resume context after async operation?

我已经从Noseratio中读到了这个问题该问题显示了一个行为,即在等待完成其操作后, TaskScheduler.Current 不同

答案指出:

如果没有执行任何实际任务 ,则TaskScheduler.CurrentTaskScheduler.Default相同

这是真的。 我已在这里看到它:

  • 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();
}

第一个Messagebox是“True”,第二个是“False”

题:

如您所见,我确实创建了一个实际任务。

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

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

并且该任务确实具有ts ,即发送的TaskScheduler.FromCurrentSynchronizationContext()

为什么第二个 MessageBox中没有保留上下文? 对我来说,斯蒂芬的回答并不清楚。

附加信息 :

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

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

它确实产生了true 但为什么 ?

混淆的原因是:

  1. UI没有“特殊” TaskScheduler 在UI线程上运行的代码的默认情况是TaskScheduler.Current存储ThreadPoolTaskSchedulerSynchronizationContext.Current存储WindowsFormsSynchronizationContext (或其他UI应用程序中的相关文件)
  2. TaskScheduler.Current中的ThreadPoolTaskScheduler并不一定意味着它是用于运行当前代码段的TaskScheduler 它还意味着TaskSchdeuler.Current == TaskScheduler.Default ,因此“没有使用TaskScheduler
  3. TaskScheduler.FromCurrentSynchronizationContext()不返回“acutal” TaskScheduler 它返回一个“代理”,可以将任务直接发布到捕获的SynchronizationContext

因此,如果您在开始任务之前(或在任何其他地方)运行测试,您将获得与等待之后相同的结果

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

因为TaskScheduler.CurrentThreadPoolTaskSchedulerTaskScheduler.FromCurrentSynchronizationContext()返回SynchronizationContextTaskScheduler

这是你的例子的流程:

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

因此,创建的任务在使用SynchronizationContext的代理TaskScheduler上运行,但是在await发布到SynchronizationContext而不是TaskScheduler之后的延续。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM