https://blog.csdn.net/iloli/article/details/16859605

简而言之就是允许一个线程和另外一个线程进行通讯,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 ,如果其它类要访问它,可将它声明为静态
SynchronizationContext sync; //----------------------------------------- //在UI线程可以接触到这个类的地方得到它
sync = SynchronizationContext.Current; //---------------------------------------- //声明一个要带回给UI线程的委托,然后使用SynchronizationContext的Send方法
//PushNewMsgEvent 是一个定义好的事件
private void PushNewMsg(object sender, InfoBox iBox)
{//这个方法是被子线程执行的。
if (PushNewMsgEvent != null)
{ SendOrPostCallback sp = new SendOrPostCallback(aaa); //声明SynchronizationContext.Send方法里需要用到的委托
List<object> tmp = new List<object>(); //将多个参数封装到一个对象中,以便给委托传递object参数
tmp.Add(sender);
tmp.Add(iBox); sync.Send(sp, tmp); //发送给主UI线程来执行sp这个委托 }
} //一个要带回给UI线程执行的委托方法
private void aaa(object obj)
{//这个方法如果没有上面的SynchronizationContext.Send,也将是在子线程中执行,但由于上面的把这个方法委托给了主UI去执行,所以它将被运行在UI线程上
List<object> tmp = (List<object>)obj;
User user = (User)tmp[0];
InfoBox ib = (InfoBox)tmp[1];
PushNewMsgEvent(user, ib); //这里已经变成在主UI线程中来触发执行这个事件了,不再是在子线程中触发。
}

···

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

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

using System;
using System.Threading; namespace NewMeteMonitor
{
/// <summary>
/// 利用SynchronizationContext进行子线程与UI线程的通讯。
/// 即向UI线程发送子线程中的内容(线程安全的方式)
/// 学习FORM窗体中的事件,将子线程封装在一个类中,并通过这个类中的事件将内容发回UI线程。
/// 2016-03-28 by bob
/// </summary>
class SyncTest
{
public event EventHandler<EventAgrsBob> BackEvent;
public EventAgrsBob myAgrs = new EventAgrsBob();
private SynchronizationContext sync = null;
private Thread subThread; public SyncTest()
{
//由于SyncTest类的对象通常是由UI来创建的,所有执行到这里时还是是UI线程中的。
//因此可以在这个类的构建方法中直接获得UI线程的线程上下文SynchronizationContext
sync = SynchronizationContext.Current; if (sync==null)
{
throw new NullReferenceException("by bob.\nSynchronizationContext对象为空。请在UI线程的方法中创建该对象,不要在非UI线程创建它,也不要在类的成员声明时直接创建该对象,因为在类的成员声明时.NET还没有构建UI线程的SynchronizationContext对象。");
}
} /// <summary>
/// 外部(UI线程)的调用入口
/// </summary>
public void Start()
{
subThread = new Thread(new ThreadStart(ThreadDoing));
subThread.Start();//子线程开始工作
} /// <summary>
/// 子线程的具体方法
/// </summary>
private void ThreadDoing()
{
//定义一个消息同步对象,它是一个委托,将封装了触发事件的方法sendmethod放进去。s
SendOrPostCallback sendDelegate = new SendOrPostCallback(sendmethod); for (int i = 0; i < 100; i++)
{
Thread.Sleep(500);
myAgrs.value = i; //本来这里应该触发事件,现在由sync.Send代为触发,从而实现与UI线程通讯。这步是子线程实现与UI通讯的关键。
sync.Send(sendDelegate, myAgrs);
//要理解上面这行代码的执行。它的参数由一个封装了事件的委托sendDelegate 及一个给委托传递的对象组成。
//sendDelegate里的方法就是在UI线程上要处理的具体工作,myAgrs封装了信息,并借由sendDelegate的参数一并发给了UI线程。
//它有这样的置换关系:
//sync.Send(sendDelegate, myAgrs)==sendDelegate(myAgrs)-->sendmethod(myAgrs)-->BackEvent(this, myAgrs)
}
} /// <summary>
/// 由于线程同步消息方法 sync.Send(SendOrPostCallback d,object state)的需要,
/// 这里必须将触发事件封装进来,因为在创建SendOrPostCallback委托对象时需要这个方法。
/// 这个方法也很简单就是触发BackEvent事件。
/// </summary>
/// <param name="obj"></param>
private void sendmethod(object obj)
{
BackEvent(this, (EventAgrsBob)obj); //触发事件
}
} /// <summary>
/// 定义一个事件参数类,只添加一个value属性 用来保存一个数。
/// 定义的参数类必须继承于EventArgs。因为在定义事件的泛型委托:EventHandler<TEventAgrs> 中有此约束:where TEventAgrs:EventAgrs
/// </summary>
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();//test内部的子线程工作时通过BackEvent事件将消息回传到这里。
} private void button1_Click(object sender, EventArgs e)
{
test.Start(); //按钮按下启动对象内部的子线程工作
}
}
}

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

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

    简而言之就是允许一个线程和另外一个线程进行通讯,SynchronizationContext在通讯中充当传输者的角色.另外这里有个地方需要清楚的,不是每个线程都附加SynchronizationCon ...

  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. navicat连接oracle报错:ORA-12737 Instant Client Light:unsupported server character set ZHS16GBK

    今天使用Navicat连接Oracle数据库,报了下面的这个错误:“ORA-12737 Instant Client Light:unsupported server character set ZH ...

  2. go socket 服务端处理多用户

    package main import ( "fmt" "net" "strings") func main() { listener, e ...

  3. python关于字典嵌套字典,列表嵌套字典根据值进行排序

    python 对于字典嵌套字典, 列表嵌套字典排序 例:列表嵌套自字典:d = [{"name": '张三', 's': 68}, {'name': '李四', 's': 97}] ...

  4. jquery操作按钮修改对应input属性

    点击右侧的修改按钮,对应的input中的disabled和readonly得属性修改 $(".buttonxg button").click(function(){ $(this) ...

  5. dede5.7评论框不显示,文章页表情不显示,解决办法

    dede5.7评论框不显示,文章页表情不显示,解决办法 进入dede后台,系统-系统基本参数-其它选项,找到模板引擎禁用标签,去掉里面的php,如图 进入dede模板目录,默认是/templets/d ...

  6. K Edit Distance

    Description Given a set of strings which just has lower case letters and a target string, output all ...

  7. H3CNE学习6 静态路由

    一.相应命令 1.查看路由表 2.直连路由 3.静态路由配置 4.路由器转发数据包 二.静态路由2 1.路由优先级 管理距离即优先级,值越小就越优先 2.路由度量 如果上下都是使用的相同的路由协议那么 ...

  8. H3CNE学习4 命令行简介

    一.命令 1.简单命令 2.给路由器接口配置IP地址 3.常用命令 4.删除配置

  9. 洛谷 P2058 海港 题解

    P2058 海港 题目描述 小K是一个海港的海关工作人员,每天都有许多船只到达海港,船上通常有很多来自不同国家的乘客. 小K对这些到达海港的船只非常感兴趣,他按照时间记录下了到达海港的每一艘船只情况: ...

  10. Go程序员面试算法宝典-读后感2-链表

    链表作为最基本的数据结构,它不仅仅在实际应用中有着非常重要的作用,而且也是程序员面试笔试必考的内容. 详情请Google吧. 1.如何实现链表的逆序 就地逆序 package main import ...