深入理解 SynchronizationContext

深入理解 SynchronizationContext

SynchronizationContext(后续以SC简称) 是什么?

1.1 概念

​ 在 .NET 框架的多线程程序中,往往很多时间需要将一个线程工作单元或上下文,传递给另一个线程。我们都知道的是 Windows 上的程序是以 消息循环为中心的,这个如何理解呢?

每一个 window 窗体都有一个与之关联的 Window Procedure,这个是一个用来处理所有消息发送或发送到类的给所有消息的函数。窗体的所有UI显示和显示都取决于 Window Procedure 对这些消息的响应。

SynchronizationContext 主要提供了一下三方面的功能:

​ 1) 提供了一种把工作单元 添加到 上下文的队列中的方法。

​ 2) 每一个线程 都有 一个 “current” 的 上下文。

​ 3) 它保存着未完成异步操作数的计数,这个数量 随着 当前的 SC 被捕获,或被夺取时增加,当捕获的 SC 用于将完成通知排队发送到该上下文时,则减少。

// 重要的 SynchronizationContext APIClass
{
  // Dispather work to the context.
  void Post(); // Asynchronously
  void send(); // Synchronously
  
  // Keep track of the number of asynchronous operations.
  void OperationStarted();
  void OperationCompleted();
  
  // Each thread has a current context.
  // If "Current" is null, then the thread's current context is
  // "new SynchronizationContext()", by convention.
  static SynchronizationContext Current{get;}
  static void SetSynchronizationContext(SynchronizationContext);
}

1.2 SynchronizationContext 的实现

1)WindowsFormsSynchronizationContext (System.Windows.Forms.dll)

​ 1,Use ISynchronizeInvoke on UI Control,用来将委托传递 给 win32 message loop

​ 2,每一个 UI 线程 都会创建一个 WindowsFormsSynchronizationContext

​ 3,WindowsFormsSynchronizationContext 的上下文是一个 单一的UI 线程

2)DispatcherSynchronizationContext(WindowsBase.dll: System.Windows.Threading)

​ 1,以 “Normal” 优先级的委托 传递给 UI 线程。

​ 2,所有排队到 DispatcherSynchronizationContext 的委托都是由 特定的UI线程 按照他们排队的顺序 依次执行,一次执行一个。

​ 3,DispatcherSynchronizationContext 的上下文是一个 单一的 UI 线程

3)Default(ThreadPool) SynchronizationContext(mscorlib.dll: System.Threading)

​ 1,默认的 SynchronizationContext 是一个默认构造函数的 SynchronizationContext 对象,按照惯例,如果一个线程 当前的 SynchronizationContext 是 null,那么它会 隐式的 含有一个 默认的 SynchronizationContext

​ 2,默认 SynchronizationContext 将它的异步委托 添加到 线程池 队列,但是 在调用的线程上执行它的同步委托。因此,它的上下文 涵盖了 所有的线程池的线程 以及 调用它的线程。上下文 “借用” 调用它的线程,然后把它们带入到上下文中 直到委托结束。从某种意义上来说,默认上下文可能包含当前进程中的任何线程。

​ 3,默认 SynchronizationContext 是应用在 线程池中的线程的除非是被 ASP.NET 托管的代码,默认的 SynchronizationContext 也隐式应用于显式的子线程中除非子线程设置了自己的 SynchronizationContext。因此,UI 的应用一般都有两个 SynchronizationContext, UI 的 SynchronizationContext cover UI thread, default SynchronizationContext cover ThreadPool thread。

图1 是一个典型比如 WPF 程序,调用 Dispatcher.InvokeDispatcher.BeginInvoke 时Context 转换的一个图。

private void On_Time_Elapsed(object sender, EventArgs e)
{
		Dispatcher.Invoke(()=>{
      _displayTextBlock.Text = "Show Here.";
    });  
}

图1

图2 是 WPF 程序中,Dispatcher.Invoke 中又新开了线程池的线程执行的例子。

private void On_Time_Elapsed(object sender, EventArgs e){
  Dispatcher.Invoke(()=>{
 			Task.Run(()=>{
        // Do Something here.
      });   
    
    _displayTextBlock.Text = "1111";
  });
}

图2

各个不同实现的 SynchronizationContext 的区别

Specific Thread Used to Execute Delegates Exclusive (Delegates Execute One at a Time) Ordered (Delegates Execute in Queue Order) Send May Invoke Delegate Directly Post May Invoke Delegate Directly
Windows Forms Yes Yes Yes If called from UI thread Never
WPF/Silverlight Yes Yes Yes If called from UI thread Never
Default No No No Always Never
ASP.NET No Yes No Always Always

参考

1) It's all about SynchronizationContext

posted @ 2021-07-12 22:44  BUTTERAPPLE  阅读(1222)  评论(0编辑  收藏  举报