简而言之就是允许一个线程和另外一个线程进行通讯,SynchronizationContext在通讯中充当传输者的角色。另外这里有个地方需要清楚的,不是每个线程都附加SynchronizationContext这个对象,只有UI线程是一直拥有的。
在多线程操作时往往需要切回某个线程中去工作,等完成后再切回来。如主UI线程中创建了一个子线程A。A中添加了委托事件。UI线程中向A线程的类注册了事件,当A线程触发事件时去修改UI上的属性如TEXT。这个时候往往要在UI线程向子线程注册的事件方法中使用控件的invoke方法才能访问UI线程中的控件,因为这些注册的事件(委托)方法代码虽然看似写在UI线程的Form类中,但实际上是注册在了子线程A的事件中,它们是会被子线程A触发事件时在子线程内部执行的。这样,我们不得不在主UI线程的类的注册事件方法中通过控件的invoke方法才能访问控件,这样做十分麻烦。我们想和系统的控件事件一样,直接在注册的事件方法中访问控件。那么我们想,有没有一个机制能让子线程在执行UI线程委托过来的(注册的事件)方法中直接访问控件本身呢?答案是肯定的。对过SynchronizationContext.Current 。
SynchronizationContext.Current 能得到当前被主UI线程接管过的对象SynchronizationContext。
这个对象有一个方法:SynchronizationContext.Send(SendOrPostCallback d,object state)
d 为一个没有返回值,并且具有一个Object类型传入参数的委托(SendOrPostCallback )
state 为执行这个委托时的参数(object)
你现在需要在子线程运行的时候利用SynchronizationContext.Current 得到SynchronizationContext对象,然后在需要切回UI线程的地方使用:
SynchronizationContext.Send(SendOrPostCallback d,object state);就可实现将委托的方法切回到UI线程上去执行。
注意:SynchronizationContext的对象不是所有线程都被附加的,只有UI主线程会被附加。对于UI线程来说,是如何将SynchronizationContext这个对象附加到线程上的呢?
在Form1 form = new Form1()之前,SynchronizationContext对象是为空,而当实例化Form1窗体后,SynchronizationContext对象就被附加到这个线程上了。所以可以得出答案了:当Control对象被创建的同时,SynchronizationContext对象也会被创建并附加到线程上。所有在使用时,一定要等窗体InitializeComponent(); 这个完成后 它才能得到一个不这NULL的对象
可以参考这里:http://lzy3169421.blog.163.com/blog/static/1135452772009357251417/
最后SynchronizationContext的Sendt()和Post()二个方法的区别:
Send() 是简单的在当前线程上去调用委托来实现(同步调用)。也就是在子线程上直接调用UI线程执行,等UI线程执行完成后子线程才继续执行。
Post() 是在线程池上去调用委托来实现(异步调用)。这是子线程会从线程池中找一个线程去调UI线程,子线程不等待UI线程的完成而直接执行自己下面的代码。
详细的推荐看这二篇文章:
线程之间的通讯---SynchronizationContext
奇妙的SynchronizationContext
- SynchronizationContext sync;
-
-
- sync = SynchronizationContext.Current;
-
-
-
-
- private void PushNewMsg(object sender, InfoBox iBox)
- {
- if (PushNewMsgEvent != null)
- {
-
- SendOrPostCallback sp = new SendOrPostCallback(aaa);
- List<object> tmp = new List<object>();
- tmp.Add(sender);
- tmp.Add(iBox);
-
- sync.Send(sp, tmp);
-
- }
- }
-
- private void aaa(object obj)
- {
- List<object> tmp = (List<object>)obj;
- User user = (User)tmp[0];
- InfoBox ib = (InfoBox)tmp[1];
- PushNewMsgEvent(user, ib);
- }
···
#################################
下面是一个自己做的例子。它将模仿WINDOWS窗口编程中的事件,由子线程触发事件,触发的事件又由消息同步发送到UI线程上来,最终在UI线程中处理事件。
- using System;
- using System.Threading;
-
- namespace NewMeteMonitor
- {
-
-
-
-
-
-
- class SyncTest
- {
- public event EventHandler<EventAgrsBob> BackEvent;
- public EventAgrsBob myAgrs = new EventAgrsBob();
- private SynchronizationContext sync = null;
- private Thread subThread;
-
- public SyncTest()
- {
-
-
- sync = SynchronizationContext.Current;
-
- if (sync==null)
- {
- throw new NullReferenceException("by bob.\nSynchronizationContext对象为空。请在UI线程的方法中创建该对象,不要在非UI线程创建它,也不要在类的成员声明时直接创建该对象,因为在类的成员声明时.NET还没有构建UI线程的SynchronizationContext对象。");
- }
- }
-
-
-
-
- public void Start()
- {
- subThread = new Thread(new ThreadStart(ThreadDoing));
- subThread.Start();
- }
-
-
-
-
- private void ThreadDoing()
- {
-
- SendOrPostCallback sendDelegate = new SendOrPostCallback(sendmethod);
-
- for (int i = 0; i < 100; i++)
- {
- Thread.Sleep(500);
- myAgrs.value = i;
-
-
- sync.Send(sendDelegate, myAgrs);
-
-
-
-
- }
- }
-
-
-
-
-
-
-
- private void sendmethod(object obj)
- {
- BackEvent(this, (EventAgrsBob)obj);
- }
- }
-
-
-
-
-
- public class EventAgrsBob : EventArgs
- {
- public int value { get; set; }
- }
-
- }
下面是UI线程 窗体中的代码:
- using System;
- using System.Windows.Forms;
-
- namespace NewMeteMonitor
- {
- public partial class Frm_Main : Form
- {
- SyncTest test = null;
-
- public Frm_Main()
- {
- InitializeComponent();
- test = new SyncTest();
- test.BackEvent += test_BackEvent;
- }
-
- private void test_BackEvent(object sender, EventAgrsBob e)
- {
- this.button1.Text = e.value.ToString();
- }
-
- private void button1_Click(object sender, EventArgs e)
- {
- test.Start();
- }
- }
- }
利用SynchronizationContext.Current在线程间同步上下文
原文:http://www.cnblogs.com/micro-chen/p/7724309.html