34

我仍在学习整个任务概念和 TPL。根据我目前的理解, SynchronizationContext 函数(如果存在)用于await在“某处”分派任务。另一方面,Task类中的函数不使用上下文,对吧?

因此,例如Task.Run(...)将始终在线程池的工作线程上分派操作并SynchronizationContext.Current完全忽略。await Foobar()之后会使用上下文执行生成的任务await吗?

如果这是真的,我的问题是:我怎样才能获得一个Task, 实际运行一个动作但使用 调度SynchronizationContext.Current.Send/Post

任何人都可以推荐一个好的介绍SynchronizationContext,尤其是框架的其余部分何时以及如何使用它们?MSDN似乎对课程非常安静。Google 的热门热门歌曲(此处此处)似乎只针对 Windows 窗体调度。Stephen Cleary 写了一篇文章,很好地了解了哪些上下文已经存在以及它们是如何工作的,但我对它们实际使用的地点和时间缺乏了解。

4

2 回答 2

49

如何获得一个实际运行一个动作但使用 SynchronizationContext.Current.Send/Post 调度的任务?

使用特殊的任务调度程序:

Task.Factory.StartNew(
    () => {}, // this will use current synchronization context
    CancellationToken.None, 
    TaskCreationOptions.None, 
    TaskScheduler.FromCurrentSynchronizationContext());

任何人都可以推荐一个很好的 SynchronizationContext 介绍

查看 Stephen Cleary 的文章It's All About the SynchronizationContext

于 2013-06-04T11:19:07.910 回答
28

在您学习这一点时,重要的是要指出Task TPLTask 使用的与 async/await 使用的完全不同,即使它们是相同的类型。例如,TPL 通常使用父/子任务,但async/await不使用。

TPL 使用任务调度程序来执行其任务。正如 Dennis 指出的那样,TaskScheduler.FromCurrentSynchronizationContext将为您提供一个任务调度程序,用于Post在当前SynchronizationContext执行其任务。

async/await通常不使用任务调度程序。我的博客上有一篇介绍性async/await帖子,其中包含上下文信息,我还在我的MSDN 文章中简要提到了它(不过很容易忽略)。本质上,当一个async方法在 a 处挂起时await,默认情况下它将捕获当前SynchronizationContext(除非它是null,在这种情况下它将捕获当前TaskScheduler)。当async方法恢复时,它会在该上下文中恢复执行。

Dennis 指出了将任务调度到 current 的 TPL 方式SynchronizationContext,但在async/ awaitworld 中,这种方式不是必需的。相反,您可以通过以下方式将任务显式调度到线程池Task.Run

async Task MyMethodAsync()
{
  // Whee, on a SynchronizationContext here!
  await Task.Run(() => { }); // Ooo, on the thread pool!
  // Back on the SynchronizationContext ...
  //  ... automagically!
}

我写SynchronizationContext这篇文章正是因为 MSDN 文档太少了。我的博客上有更多信息,但所有重要信息都在 MSDN 文章中。许多类型使用AsyncOperation而不是SynchronizationContext直接使用;最好的文档隐藏在 EAP 文档(“线程和上下文”部分)下。但我还应该指出,由于async/ await,EAP 实际上已经过时,所以我不会使用AsyncOperation(or SynchronizationContext) 编写代码 - 除非我实际上是在编写自己的 SynchronizationContext.

于 2013-06-04T12:02:40.513 回答