SynchronizationContext和TaskScheduler之间的概念区别是什么

时间:2012-03-06 08:16:23

标签: c# multithreading task-parallel-library system.reactive conceptual

Stephen Toub blogged

  

SynchronizationContext和TaskScheduler都是抽象的   代表一个“调度程序”,你给它一些工作,它   确定运行该工作的时间和地点。有很多不同   调度程序的形式。例如,ThreadPool是一个调度程序:你   调用ThreadPool.QueueUserWorkItem来提供一个要运行的委托   委托排队,最终成为ThreadPool的一个线程   拿起并运行该代表。您的用户界面也有   scheduler:消息泵。

因此System.Reactive.Concurrency.EventLoopSchedulerDispatcherThreadPoolTaskSchedulerSyncrhonizationContextIScheduler implementations of Reactive Extensions都是“调度程序”。

他们之间有什么区别?

为什么他们都必要?我想我得到EventLoop,Dispatcher,ThreadPool。 IScheduler也有很好的解释 但是TaskScheduler和SyncrhonizationContext仍然不清楚。

Stephen Cleary's excellent article解释了SyncrhonizationContext,我想我明白了。为什么我们需要TaskScheduler,目前尚不清楚。

请解释或指向消息来源。

4 个答案:

答案 0 :(得分:11)

每个平台都有自己的“调度程序”,并且它们周围有自己的抽象。例如WinForms使用消息泵。 WPF使用“Dispatcher”中抽象的另一个消息泵。 ThreadPool是在“ThreadPool”中抽象的另一个“调度程序”。这些(以及其他一些)是较低级别的调度程序。

任务和TaskScheduler希望任务的用户不必考虑在这些较低级别安排任务(当然,您可以以抽象的方式)。您应该能够启动任务,环境“调度程序”应该处理它。例如,无论我在哪个平台上运行,TaskFactory.StartNew(()=>{LengthyOperation()})都应该有效。这就是SynchronizationContext的用武之地。它知道当前运行的框架中涉及哪些低级调度程序。这传递给TaskScheduler,并且调度程序可以调度任务(可能在ThreadPool上)并通过与当前运行的框架相关联的较低级别调度程序(请参阅SynchronizationContext)来安排继续同步要求。例如虽然您希望您的任务在ThreadPool中运行,但您可能希望继续在UI线程中运行。

知道TaskScheduler是多个其他调度程序的抽象是很重要的。这不是它存在的唯一原因,而是这种“额外”抽象的原因之一“。

答案 1 :(得分:8)

虽然如引用,

  

SynchronizationContext和TaskScheduler都是抽象的   代表“调度程序”

IMO,抽象程度(以及API)不同。在某种意义上,SynchronizationContext是一种更通用的API,Post / Send采用简单的方法委托。

另一方面,TaskScheduler是一个特定于TPL的抽象 - 因此它提供了QueueTask等处理Task对象的方法。使用同步上下文而不是任务调度程序(即具有SynchronizationContext的TPL特定实现)会使得处理任务调度变得更加繁琐(当然,它将是TPL上下文中的弱类型API)。因此,TPL设计人员选择对抽象调度程序API进行建模,这对于TPL是有意义的(无论如何,这是抽象的目的 - 对吗?) - 当然,为了弥补差距,FCL包含一个内部类SynchronizationContextTaskScheduler这是在SynchronizationContext上的包装器TaskScheduler实现。

SynchronizationContext是在.NET 2.0中引入的,而TPL是在.NET 4中引入的。有趣的是,如果序列是相反的,那么FCL设计者会选择什么,即如果TPL在此时存在则该怎样。 NET 2.0。通过将delgates建模为特定专业化中的任务,可以使用IMO,TaskScheduler代替SynchrinizationContext。

答案 2 :(得分:6)

我刚刚读了杰弗里·里切(Jeffrey Ritcher)的CLR via C#书,感谢他,我也可以给出一些与该主题相关的简单解释。 (假设我不完全同意答案中的全部细节)

首先,TaskScheduler对象负责执行计划的任务。 FCL附带两种TaskScheduler派生的类型: 线程池任务计划程序 同步上下文任务计划程序 < / strong>。默认情况下,所有应用程序都使用 线程池任务计划程序。该任务计划程序将任务计划到线程池的工作线程中。您可以通过查询TaskScheduler的静态Default属性来获得对默认任务计划程序的引用。

同步上下文任务计划程序通常用于具有图形用户界面的应用程序。此任务计划程序将所有任务计划到应用程序的GUI线程上 这样所有任务代码都可以成功更新UI组件,例如按钮,菜单项等。 同步上下文任务计划程序根本不使用线程池。您可以通过查询TaskScheduler的静态FromCurrentSynchronizationContext方法来获取对同步上下文任务计划程序的引用。

SynchronizationContextTaskScheduler实现中可以看到,在内部它使用SynchronizationContext字段。 FCL定义了一个名为System.Threading.SynchronizationContext的基类,它解决了所有这些问题:

  • GUI应用程序强加了一个线程模型,其中 创建一个UI元素是唯一允许更新该UI的线程 元件。这是一个问题,因为您的代码将引发异常 如果它尝试通过线程池线程更新UI元素。不知何故, 线程池线程必须具有GUI线程才能更新UI 元素。
  • ASP.NET应用程序允许任何线程执行其所需的任何操作。当线程池线程开始处理客户的请求时,它可以 假定客户的文化,允许网络服务器返回 数字,日期和时间的区域性特定格式。在 此外,网络服务器可以假定客户的身份,以便 服务器只能访问允许客户端访问的资源 访问。当线程池线程产生异步操作时, 它可能由另一个线程池线程完成,它将是 处理异步操作的结果。虽然这项工作 代表原始客户要求执行, 文化和身份需要“流入”新的线程池线程,因此 代表客户完成的任何其他工作均使用 客户的文化和身份信息。

简单地说, SynchronizationContext派生的对象将应用程序模型连接到其线程模型 。 FCL定义了几个派生自 SynchronizationContext,但是通常您不会直接处理这些类。实际上,其中许多没有公开暴露或记录在案。

在大多数情况下,应用程序开发人员无需了解有关SynchronizationContext类的任何知识。等待任务时,调用线程的SynchronizationContext 获得对象。当线程池线程完成任务时,SynchronizationContext 使用对象,确保为您的应用程序模型使用正确的线程模型。因此,当 GUI线程 awaits是一个任务,可以保证await运算符之后的代码可以在GUI线程上执行,如下所示: ,允许该代码更新UI元素。对于ASP.NET应用程序, 确保await运算符在具有客户端区域性和 与之相关的主要信息

如果您有特殊任务,当然可以定义从TaskScheduler派生的自己的类 调度需求。 Microsoft提供了许多用于任务的示例代码,并包括了源代码 Parallel Extensions Extras包中的一堆任务调度程序的代码。像IOTaskSchedulerLimitedConcurrencyLevelTaskSchedulerOrderedTaskSchedulerPrioritizingTaskSchedulerThreadPerTaskScheduler

答案 3 :(得分:-1)

简而言之,SynchronizationContext是关于“线程”的抽象  上下文”,而TaskScheduler是关于“基于任务的时间表”的抽象-故事的结尾。

因此,人们无需担心不同框架用于同一工作的特定接口即可操作线程:例如WinForms中的Control.BeginInvoke()和WPF中的Dispatcher.BeginInvoke,但只考虑了一个通用原则:“ UI更新应在某些特殊的线程上下文中进行:UI线程”

另一方面,通过使用TaskScheduler,人们可以专注于任务的实现,而不必担心如何线性或并行执行任务。

一些有用的文章: