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

Mic*_*riv 26 c# multithreading conceptual task-parallel-library system.reactive

Stephen Toub在博客写道

SynchronizationContext和TaskScheduler都是表示"调度程序"的抽象,是您提供一些工作的东西,它决定了运行该工作的时间和地点.有许多不同形式的调度程序.例如,ThreadPool是一个调度程序:您调用ThreadPool.QueueUserWorkItem来提供一个委托来运行,该委托被排队,并且其中一个ThreadPool的线程最终选择并运行该委托.您的用户界面还有一个调度程序:消息泵.

因此System.Reactive.Concurrency.EventLoopScheduler,Reactive Extensions的Dispatcher,ThreadPool,TaskScheduler,SyncrhonizationContextIScheduler实现在这个意义上都是"调度程序".

他们之间有什么区别?

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

Stephen Cleary的优秀文章解释了SyncrhonizationContext,我想我明白了.那么为什么我们需要TaskScheduler,目前尚不清楚.

请解释或指向消息来源.

Pet*_*hie 12

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

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

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

  • @variable 您需要对 UI 执行的任何操作(例如更改控件值或更改可见内容)都需要在 UI 线程上执行。 (2认同)

Vin*_*ayC 9

虽然,如引用,

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设计人员会选择什么,即如果在.NET 2.0时存在TPL会怎么样.通过将delgates建模为特定专业化中的任务,可以使用IMO,TaskScheduler代替SynchrinizationContext.

  • SynchronizationContext更多地是关于执行关联性的要求.例如,如果要更新控件,则需要在UI线程上执行此操作.该要求在SychronizationContext中被抽象.在TaskScheduler中抽象出各个任务如何相互安排以及它们在何处运行(可能需要同步*).相关但正交的抽象. (3认同)
  • @PeterRitchie:所以,SynchroContext 是关于“哪里”,而调度程序是关于“哪里”和“什么顺序”。因此,它们**不**正交。如果调度程序只是关于“什么顺序”,它们将是真正正交的。事实上,它们只是重叠。我个人喜欢将 SynCtx 视为用于实现(任务)调度程序的“低级”“特定于案例”的构建块。但不是正交的! (3认同)

Far*_*yev 9

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

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

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

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

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

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

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

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