简而言之就是允许一个线程和另外一个线程进行通讯,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

  1. //在类里声明SynchronizationContext ,如果其它类要访问它,可将它声明为静态
  2. SynchronizationContext sync;
  3. //-----------------------------------------
  4. //在UI线程可以接触到这个类的地方得到它
  5. sync = SynchronizationContext.Current;
  6. //----------------------------------------
  7. //声明一个要带回给UI线程的委托,然后使用SynchronizationContext的Send方法
  8. //PushNewMsgEvent 是一个定义好的事件
  9. private void PushNewMsg(object sender, InfoBox iBox)
  10. {//这个方法是被子线程执行的。
  11. if (PushNewMsgEvent != null)
  12. {
  13. SendOrPostCallback sp = new SendOrPostCallback(aaa);    //声明SynchronizationContext.Send方法里需要用到的委托
  14. List<object> tmp = new List<object>(); //将多个参数封装到一个对象中,以便给委托传递object参数
  15. tmp.Add(sender);
  16. tmp.Add(iBox);
  17. sync.Send(sp, tmp);    //发送给主UI线程来执行sp这个委托
  18. }
  19. }
  20. //一个要带回给UI线程执行的委托方法
  21. private void aaa(object obj)
  22. {//这个方法如果没有上面的SynchronizationContext.Send,也将是在子线程中执行,但由于上面的把这个方法委托给了主UI去执行,所以它将被运行在UI线程上
  23. List<object> tmp = (List<object>)obj;
  24. User user = (User)tmp[0];
  25. InfoBox ib = (InfoBox)tmp[1];
  26. PushNewMsgEvent(user, ib); //这里已经变成在主UI线程中来触发执行这个事件了,不再是在子线程中触发。
  27. }

···

#################################

下面是一个自己做的例子。它将模仿WINDOWS窗口编程中的事件,由子线程触发事件,触发的事件又由消息同步发送到UI线程上来,最终在UI线程中处理事件。

  1. using System;
  2. using System.Threading;
  3. namespace NewMeteMonitor
  4. {
  5. /// <summary>
  6. /// 利用SynchronizationContext进行子线程与UI线程的通讯。
  7. /// 即向UI线程发送子线程中的内容(线程安全的方式)
  8. /// 学习FORM窗体中的事件,将子线程封装在一个类中,并通过这个类中的事件将内容发回UI线程。
  9. /// 2016-03-28 by bob
  10. /// </summary>
  11. class SyncTest
  12. {
  13. public event EventHandler<EventAgrsBob> BackEvent;
  14. public EventAgrsBob myAgrs = new EventAgrsBob();
  15. private SynchronizationContext sync = null;
  16. private Thread subThread;
  17. public SyncTest()
  18. {
  19. //由于SyncTest类的对象通常是由UI来创建的,所有执行到这里时还是是UI线程中的。
  20. //因此可以在这个类的构建方法中直接获得UI线程的线程上下文SynchronizationContext
  21. sync = SynchronizationContext.Current;
  22. if (sync==null)
  23. {
  24. throw new NullReferenceException("by bob.\nSynchronizationContext对象为空。请在UI线程的方法中创建该对象,不要在非UI线程创建它,也不要在类的成员声明时直接创建该对象,因为在类的成员声明时.NET还没有构建UI线程的SynchronizationContext对象。");
  25. }
  26. }
  27. /// <summary>
  28. /// 外部(UI线程)的调用入口
  29. /// </summary>
  30. public void Start()
  31. {
  32. subThread = new Thread(new ThreadStart(ThreadDoing));
  33. subThread.Start();//子线程开始工作
  34. }
  35. /// <summary>
  36. /// 子线程的具体方法
  37. /// </summary>
  38. private void ThreadDoing()
  39. {
  40. //定义一个消息同步对象,它是一个委托,将封装了触发事件的方法sendmethod放进去。s
  41. SendOrPostCallback sendDelegate = new SendOrPostCallback(sendmethod);
  42. for (int i = 0; i < 100; i++)
  43. {
  44. Thread.Sleep(500);
  45. myAgrs.value = i;
  46. //本来这里应该触发事件,现在由sync.Send代为触发,从而实现与UI线程通讯。这步是子线程实现与UI通讯的关键。
  47. sync.Send(sendDelegate, myAgrs);
  48. //要理解上面这行代码的执行。它的参数由一个封装了事件的委托sendDelegate 及一个给委托传递的对象组成。
  49. //sendDelegate里的方法就是在UI线程上要处理的具体工作,myAgrs封装了信息,并借由sendDelegate的参数一并发给了UI线程。
  50. //它有这样的置换关系:
  51. //sync.Send(sendDelegate, myAgrs)==sendDelegate(myAgrs)-->sendmethod(myAgrs)-->BackEvent(this, myAgrs)
  52. }
  53. }
  54. /// <summary>
  55. /// 由于线程同步消息方法 sync.Send(SendOrPostCallback d,object state)的需要,
  56. /// 这里必须将触发事件封装进来,因为在创建SendOrPostCallback委托对象时需要这个方法。
  57. /// 这个方法也很简单就是触发BackEvent事件。
  58. /// </summary>
  59. /// <param name="obj"></param>
  60. private void sendmethod(object obj)
  61. {
  62. BackEvent(this, (EventAgrsBob)obj); //触发事件
  63. }
  64. }
  65. /// <summary>
  66. /// 定义一个事件参数类,只添加一个value属性 用来保存一个数。
  67. /// 定义的参数类必须继承于EventArgs。因为在定义事件的泛型委托:EventHandler<TEventAgrs> 中有此约束:where TEventAgrs:EventAgrs
  68. /// </summary>
  69. public class EventAgrsBob : EventArgs
  70. {
  71. public int value { get; set; }
  72. }
  73. }

下面是UI线程 窗体中的代码:

  1. using System;
  2. using System.Windows.Forms;
  3. namespace NewMeteMonitor
  4. {
  5. public partial class Frm_Main : Form
  6. {
  7. SyncTest test = null;
  8. public Frm_Main()
  9. {
  10. InitializeComponent();
  11. test = new SyncTest();
  12. test.BackEvent += test_BackEvent;
  13. }
  14. private void test_BackEvent(object sender, EventAgrsBob e)
  15. {
  16. this.button1.Text = e.value.ToString();//test内部的子线程工作时通过BackEvent事件将消息回传到这里。
  17. }
  18. private void button1_Click(object sender, EventArgs e)
  19. {
  20. test.Start(); //按钮按下启动对象内部的子线程工作
  21. }
  22. }
  23. }

利用SynchronizationContext.Current在线程间同步上下文的更多相关文章

  1. 利用SynchronizationContext.Current在线程间同步上下文(转)

    https://blog.csdn.net/iloli/article/details/16859605 简而言之就是允许一个线程和另外一个线程进行通讯,SynchronizationContext在 ...

  2. c++11 线程间同步---利用std::condition_variable实现

    1.前言 很多时候,我们在写程序的时候,多多少少会遇到下面种需求 一个产品的大致部分流程,由工厂生产,然后放入仓库,最后由销售员提单卖出去这样. 在实际中,仓库的容量的有限的,也就是说,工厂不能一直生 ...

  3. C#线程间同步无法关闭

    用C#做了个线程间同步的小程序,但每次关闭窗口后进程仍然在,是什么原因? 解决方法: 要加一句 线程.IsBackground = true; 否则退出的只是窗体 上面的方法没看懂... MSDN上说 ...

  4. Linux系统编程(29)——线程间同步(续篇)

    线程间的同步还有这样一种情况:线程A需要等某个条件成立才能继续往下执行,现在这个条件不成立,线程A就阻塞等待,而线程B在执行过程中使这个条件成立了,就唤醒线程A继续执行.在pthread库中通过条件变 ...

  5. linux线程间同步方式汇总

    抽空做了下linux所有线程间同步方式的汇总(原生的),包含以下几个: 1, mutex 2, condition variable 3, reader-writer lock 4, spin loc ...

  6. conditon_variable(条件变量)用于线程间同步

    conditon_variable(条件变量)用于线程间同步 condition_variable有5个函数,函数名及对应的功能如下: wait阻塞自己,等待唤醒 wait_for阻塞自己,等待唤醒, ...

  7. rtt学习之线程间同步与通信

    一 线程间的同步与互斥:信号量.互斥量.实践集 线程互斥是指对于临界区资源访问的排它性,如多个线程对共享内存资源的访问,生产消费型对产品的操作.临界区操作操作方法有: rt_hw_interrupt_ ...

  8. 【C#】【Thread】SynchronizationContext线程间同步

    SynchronizationContext在通讯中充当传输者的角色,实现功能就是一个线程和另外一个线程的通讯. 需要注意的是,不是每个线程都附加SynchronizationContext这个对象, ...

  9. Java:多线程,使用同步锁(Lock)时利用Condition类实现线程间通信

    如果程序不使用synchronized关键字来保证同步,而是直接使用Lock对象来保证同步,则系统中不存在隐式的同步监视器,也就不能用wait().notify().notifyAll()方法进行线程 ...

随机推荐

  1. Ext ComboBox 动态查询

    Ext中的combobox有属性typeAhead:true 可以实现模糊匹配,但是是从开始匹配的,如果需要自定的的匹配,则需要监听beforequery方法,实现自己的匹配查询方法: var gfx ...

  2. 【linux】centos7终端中文显示乱码,命令返回中文乱码

    centos7终端中文显示乱码,命令返回中文乱码 1.查看服务器编码的命令 1.1 echo $LANG 1.2 locale 1.3 查看终端xshell编码 如果以上的三点依旧保持一致,而依旧乱码 ...

  3. C\C++各路高手以及操作系统专家请进来杀死这个进程

    通常情况下编写一个程序,能够点击关闭button正常结束程序,也能够使用任务管理器结束任务,还能够使用taskkill等命令杀死进程,实在都不行也能够直接重新启动计算机. 可是,这些方法真的都管用吗? ...

  4. easyui input设置为disabled提交后获取不到属性值

    在做网站管理后台的用户修改功能时,由于当前用户修改个人信息时规定用户名不能修改,故使用了input标签的disabled属性,但是在提交数据后却发现用户名显示为空了.后 来一查才知道input设置为d ...

  5. 关于JSONP以及跨域相关

    什么是跨域: 浏览器对ajax请求的限制,不允许跨域请求资源. http://www.a.com--->http://www.b.com       是跨域 http://www.a.com-- ...

  6. [PHP] ubuntu 16.04系统下解决MySQL 5.7版本的root用户重置密码问题

    reference to : http://www.cnblogs.com/roadofstudy/p/7446690.html 最近在ubuntu系统上安装了MySQL,但是安装时没有提示输入roo ...

  7. 物联网(IoT)的11大云平台:AWS、Azure、谷歌云、Oracle、

    物联网(IoT)的11大云平台:AWS.Azure.谷歌云.Oracle. 2018-11-06 14:02 云技术 关键词:物联网AzureGoogleSalesforce云计算 导读:现在,我们将 ...

  8. Informatica 常用组件Source Qualifier之八 会话前和会话后 SQL

      可以在源限定符转换的"属性"选项卡中添加会话前和会话后 SQL 命令.您可能要使用会话前 SQL 以在会话开始时将时间标识行写入源表. PowerCenter 在读取源之前对源 ...

  9. IOS学习笔记02---语言发展概述,计算机语言简介.

    IOS学习笔记02---语言发展概述,计算机语言简介. ------------------------------------------------------------------------ ...

  10. 以上帝模式管理Windows系统

    上帝模式,,即"God Mode",或称为"完全控制面板".是Windows Vista/7系统中隐藏的一个简单的文件夹窗口,但包含了几乎所有Windows系统 ...