项目场景:
项目场景:WindowsFrom或WPF项目中不方便使用到控件的Invoke方法时,可以使用WindowsBase.dll下的System.Thread.SynchronizationContext
问题描述
很多人不了解什么是主线程,即具有Context的线程,他的设计是为了提高用户与屏幕的交互效率,进而提高用户体验。
原因分析:
有时候我们开发的桌面UI应用莫名卡顿了,一般是其他数据来源
属于其他线程
,但我们使用其他线程操作UI时就会出现卡顿的情况。
解决方案:
具体解决方案:
- 首先定义可同步的主线程
SynchronizationContext
上下文引用介质
SynchronizationContext SyncContext = null;
- 第二步:在需要的地方使用上下文引用介质
SyncContext
的Post方法
protected void initPortReceive()
{
SyncContext.Post(change, "Subscription receiving catch error");
}
- 第三步:UI中显示数据
private void change(object str)
{
showTips(str.ToString());
}
桌面应用运行效果如下:
测试运行36小时,未发生异常.且软件效果流畅。
原理解析
概念
在 .NET 框架的多线程程序中,往往很多时间需要将一个线程工作单元或上下文,传递给另一个线程。我们都知道的是 Windows 上的程序是以 消息循环为中心的,这个如何理解呢?
每一个 window 窗体都有一个与之关联的
Window Procedure
这个是一个用来处理所有消息发送或发送到类的给所有消息的函数。窗体的所有UI显示
和显示都取决于 Window Procedure
对这些消息的响应。
SynchronizationContext 主要提供了一下三方面的功能:
-
提供了一种把工作单元 添加到 上下文的队列中的方法。
-
每一个线程 都有 一个 “current” 的 上下文。
-
它保存着未完成异步操作数的计数
计数
这个数量 随着 当前的 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);
}
SynchronizationContext 的实现
WindowsFormsSynchronizationContext (System.Windows.Forms.dll)
-
Use ISynchronizeInvoke on UI Control,用来将委托传递 给 win32 message loop
-
每一个 UI 线程 都会创建一个 WindowsFormsSynchronizationContext
-
WindowsFormsSynchronizationContext 的上下文是一个 单一的UI 线程
DispatcherSynchronizationContext(WindowsBase.dll: System.Windows.Threading)
-
以 “Normal” 优先级的委托 传递给 UI 线程。
-
所有排队到 DispatcherSynchronizationContext 的委托都是由 特定的UI线程 按照他们排队的顺序 依次执行,一次执行一个。
-
DispatcherSynchronizationContext 的上下文是一个 单一的 UI 线程
Default(ThreadPool) SynchronizationContext(mscorlib.dll: System.Threading)
-
默认的 SynchronizationContext 是一个默认构造函数的 SynchronizationContext 对象,按照惯例,如果一个线程 当前的 SynchronizationContext 是 null,那么它会 隐式的 含有一个 默认的 SynchronizationContext。
-
默认 SynchronizationContext 将它的异步委托 添加到 线程池 队列,但是 在调用的线程上执行它的同步委托。因此,它的上下文 涵盖了 所有的线程池的线程 以及 调用它的线程。上下文 “借用” 调用它的线程,然后把它们带入到上下文中 直到委托结束。从某种意义上来说,默认上下文可能包含当前进程中的任何线程。
-
默认 SynchronizationContext 是应用在 线程池中的线程的除非是被 ASP.NET 托管的代码,默认的 SynchronizationContext 也隐式应用于显式的子线程中除非子线程设置了自己的 SynchronizationContext。因此,UI 的应用一般都有两个 SynchronizationContext, UI 的 SynchronizationContext cover UI thread, default SynchronizationContext cover ThreadPool thread。
图1 是一个典型比如 WPF 程序,调用 Dispatcher.Invoke 或 Dispatcher.BeginInvoke 时Context 转换的一个图。
private void On_Time_Elapsed(object sender, EventArgs e)
{
Dispatcher.Invoke(()=>{
_displayTextBlock.Text = "Show Here.";
});
}
图2 是 WPF 程序中,Dispatcher.Invoke 中又新开了线程池的线程执行的例子。
private void On_Time_Elapsed(object sender, EventArgs e){
Dispatcher.Invoke(()=>{
Task.Run(()=>{
// Do Something here.
});
_displayTextBlock.Text = "1111";
});
}