WinForm跨线程访问问题

序言

方案一:去掉线程访问主线程UI控件的安全检查(不推荐)

Control.CheckForIllegalCrossThreadCalls = false;

方案二:使用委托,将对主线程的UI控件操作推送了该线程的消息队列里,使用的方法为:Invoke方法和BeginInvoke方法,前一个是同步方法,后一个为异步方法;

使用async 关键字可避免主线程等待

this.BeginInvoke(new Action(async () =>
              {
                  label1.Text = $"Count:{Count},Time: {DateTime.Now.ToString()} ";
                  await Task.Delay(5000);
                  label1.Text = $"Finished {DateTime.Now.ToString()} ";
              }));

方案三:使用同步上下文:SynchronizationContext方法,该方法是取得主线程的上下文信息,然后在子线程将访问UI控件方法推送到UI上下文的消息队列里,使用POST或者Send;

方案四:在命名空间:   System.ComponentModel 里有个BackgroundWorker类,它是在一个单独的线程里执行

C#中禁止跨线程直接访问控件,InvokeRequired是为了解决这个问题而产生的,当一个控件的InvokeRequired属性值为真时,说明有一个创建它以外的线程想访问它。

 子线程更新主线程方法

Control的Invoke和BeginInvoke

Application.DoEvents();

Application.DoEvents可以防止界面停止响应

 private void button1_Click(object sender, EventArgs e)
        {
            for (int i = 0; i < 10000; i++)
            {
                label1.Text = i.ToString();
                Application.DoEvents();
            }
        }

测试了一下,没有Application.DoEvents()的时候,Label基本处于假死机状态,最后显示一个9999,加上后会数字变换正常显示。

如果没有加上DoEvents的话,由于循环时间会比较久就会出现假死的状态,而且程序不能处理其他的事件。而如果加上DoEvents的话就会对文本框的值实时响应,给用户带来较好的用户体验,可是DoEvents也带来了效率上的问题,处理同样的一个事件调用了DoEvents后效率降低了好几倍,这也是为什么要慎用的原因了。

理解C#中的ExecutionContext vs SynchronizationContext

为什么需要SynchronizationContext

众所周知,.NET Framework 支持几种不同类型的应用程序,而每种应用程序所支持的线程模型也不相同。

Console、Windows Service应用程序不对线程做任何限制,即在这两种应用程序中,线程可做任何它想做的事;

而Windows Forms(从.NET Framework 2.0开始)、WPF、Silverlight支持的线程模型是:窗体控件只允许创建它的线程可以对其进行更新。

如果是非创建线程对其更新,在VS中调试时,则会抛出InvalidOperationException异常,并提示:从不是创建控件的线程访问它。

虽然在非调试状态下不会抛出这个异常,但这样做不是线程安全的。

在Windows Forms中,为了解决从非创建线程更新的问题,我们可以通过调用Form.CheckForIllegalCrossThreadCalls属性并将其值设为false,

正如前面所说,这不是线程安全的,所以不建议这样做。于是MS为我们提供了一种新的更新方式:通过委托转到创建线程进行更新。

什么是SynchronizationContext

SynchronizationContext在通讯中充当传输者的角色,实现功能就是一个线程和另外一个线程的通讯。

需要注意的是,不是每个线程都附加SynchronizationContext这个对象,只有UI线程是一直拥有的。故获取SynchronizationContext也只能在UI线程上进行SynchronizationContext context = SynchronizationContext.Current;

资料

SynchronizationContext对Windows Forms窗体控件的更新方法

SynchronizationContext线程间同步

线程之间的通讯---SynchronizationContext

https://www.cnblogs.com/kiminozo/archive/2012/02/06/2340609.html

https://www.cnblogs.com/xiaoxiaotank/p/13666913.html

https://www.cnblogs.com/mqxs/p/4288644.html

原文地址:https://www.cnblogs.com/cnki/p/15516997.html