信号同步

摘要:
而在引用类型上的等待机制,则分为两类:锁定和信号同步。这是最常用的同步技术;本建议我们讨论的是信号同步。信号同步机制中涉及的类型都继承自抽象类WaitHandle,这些类型有EventWaitHandle和Semaphore以及Mutex。EventWaitHandle和Semaphore提供的都是单应用程序域内的线程同步功能,Mutex则不同,它为我们提供了跨应用程序域阻塞和解除阻塞线程的能力。tWork接收到这个信号,开始继续工作。

转自:http://www.cnblogs.com/luminji/archive/2011/05/03/2034890.html

所谓线程同步,就是多个线程之间在某个对象上执行等待(也可理解为锁定该对象),直到该对象被解除锁定。C#中对象的类型分为引用类型和值类型。CLR在这两种类型上的等待是不一样的。我们可以简单的理解为在CLR中,值类型是不能被锁定的,也即:不能在一个值类型对象上执行等待。而在引用类型上的等待机制,则分为两类:锁定和信号同步。

锁定,使用关键字lock和类型Monitor。两者没有实质区别,前者其实是后者的语法糖。这是最常用的同步技术;

本建议我们讨论的是信号同步。信号同步机制中涉及的类型都继承自抽象类WaitHandle,这些类型有EventWaitHandle(类型化为AutoResetEvent、ManualResetEvent)和Semaphore以及Mutex。见类图6-3:

clip_image002

图 同步功能类类图

EventWaitHandle(子类为AutoResetEvent、ManualResetEvent)和Semaphore以及Mutex都继承自WaitHandle,所以它们底层的原理是一致的,维护的都是一个系统内核句柄。不过我们仍需简单的区分下这三类类型。

EventWaitHandle,维护一个由内核产生的布尔类型对象(我们称之为“阻滞状态”),如果其值为false,那么在它上面等待的线程就阻塞。可以调用类型的Set方法将其值设置为true,解除阻塞。EventWaitHandle类型的两个子类AutoResetEvent和ManualResetEvent,它们的区别并不大,本建议接下来会针对它们阐述如何正确使用信号量。

Semaphore,维护一个由内核产生的整型变量,如果其值为0,则在它上面等待的线程就阻塞,其值大于0,就解除阻塞,同时,每解除阻塞一个线程,其值就减1。

EventWaitHandle和Semaphore提供的都是单应用程序域内的线程同步功能,Mutex则不同,它为我们提供了跨应用程序域阻塞和解除阻塞线程的能力。


1:使用信号机制提供线程同步的一个简单的例子

使用信号机制提供线程同步的一个简单的例子如下:

复制代码
AutoResetEvent autoResetEvent =new AutoResetEvent(false);
privatevoid buttonStartAThread_Click(object sender, EventArgs e)
{
Thread tWork
=new Thread(() =>
{
label1.Text
="线程启动..."+ Environment.NewLine;
label1.Text
+="开始处理一些实际的工作"+ Environment.NewLine;
//省略工作代码
label1.Text +="我开始等待别的线程给我信号,才愿意继续下去"+ Environment.NewLine;
autoResetEvent.WaitOne();
label1.Text
+="我继续做一些工作,然后结束了!";
//省略工作代码
});
tWork.IsBackground
=true;
tWork.Start();
}
privatevoid buttonSet_Click(object sender, EventArgs e)
{
//给在autoResetEvent上等待的线程一个信号
autoResetEvent.Set();
}
复制代码

这是一个简单的Winform窗体程序,其中一个按钮负责开启一个新的线程,还有一个按钮负责给刚开启的那个线程发送信号。现在详细解释这里面发生的事情。

AutoResetEvent autoResetEvent =new AutoResetEvent(false);

这段代码创建了一个同步类型对象autoResetEvent,它设置自己的默认阻滞状态是false。这意味着任何在它上面进行等待的线程将会被阻滞。所谓进行等待,就是在线程中应用:

autoResetEvent.WaitOne();

这说明tWork开始在autoResetEvent上等待任何其它地方给它的信号。信号来了,则tWork开始继续工作,否则就一直等着(即阻滞)。接下来我们看到在主线程中(本例中即UI线程,它相对线程tWork来说,就是一个“另外的线程”):

autoResetEvent.Set();

主线程通过上面这句代码负责向在autoResetEvent上等待的线程tWork上下文发送信号,即将tWork的阻滞状态设置为true。tWork接收到这个信号,开始继续工作。

这个例子相当简单,但是已经完整说明了信号机制的工作原理。


2:AutoResetEvent和ManualResetEvent的区别

AutoResetEvent和ManualResetEvent有这样的区别:前者在发送信号完毕后(即调用Set方法),自动将自己的阻滞状态设置为false,而后者需要进行手动设定。可以通过一个例子来说明这种区别:

复制代码
AutoResetEvent autoResetEvent =new AutoResetEvent(false);
privatevoid buttonStartAThread_Click(object sender, EventArgs e)
{
StartThread1();
StartThread2();
}
privatevoid StartThread1()
{
Thread tWork1
=new Thread(() =>
{
label1.Text
="线程1启动..."+ Environment.NewLine;
label1.Text
+="开始处理一些实际的工作"+ Environment.NewLine;
//省略工作代码
label1.Text +="我开始等待别的线程给我信号,才愿意继续下去"+ Environment.NewLine;
autoResetEvent.WaitOne();
label1.Text
+="我继续做一些工作,然后结束了!";
//省略工作代码
});
tWork1.IsBackground
=true;
tWork1.Start();
}
privatevoid StartThread2()
{
Thread tWork2
=new Thread(() =>
{
label2.Text
="线程2启动..."+ Environment.NewLine;
label2.Text
+="开始处理一些实际的工作"+ Environment.NewLine;
//省略工作代码
label2.Text +="我开始等待别的线程给我信号,才愿意继续下去"+ Environment.NewLine;
autoResetEvent.WaitOne();
label2.Text
+="我继续做一些工作,然后结束了!";
//省略工作代码
});
tWork2.IsBackground
=true;
tWork2.Start();
}
privatevoid buttonSet_Click(object sender, EventArgs e)
{
//给在autoResetEvent上等待的线程一个信号
autoResetEvent.Set();
}
复制代码

这个例子的本意是要让新起的两个工作线程tWork1和tWork2都阻滞起来,直到收到主线程的信号再继续工作。结果程序运行的结果是,只有一个工作线程继续工作,另外一个工作线程则继续保持阻滞状态。我想原因大家都已经想到了。由于AutoResetEvent在发送信号完毕就在内核中自动将自己的状态设置回false了,所以另外一个工作线程相当于根本没有收到主线程的信号。

要修正这个问题,可以使用ManualResetEvent。大家可以换成ManualResetEvent试一下。


3:应用实例

最后,再举一个需要用到线程同步的实际例子:模拟网络通信。客户端在运行过程中,服务器每隔一段的时间会给客户端发送心跳数据。实际工作中服务器和客户端会是网络中两台不同的终端,在这个例子中我们进行了简化。工作线程tClient模拟客户端,主线程(UI线程)模拟服务器端。客户端每3秒检测是否收到服务器的心跳数据,如果没有心跳数据,则显示网络连接断开。代码如下:

复制代码
AutoResetEvent autoResetEvent =new AutoResetEvent(false);
privatevoid buttonStartAThread_Click(object sender, EventArgs e)
{
Thread tClient
=new Thread(() =>
{
while (true)
{
//等3秒,3秒没有信号,显示断开
//有信号,则显示更新
bool re = autoResetEvent.WaitOne(3000);
if (re)
{
label1.Text
=string.Format("时间:{0},{1}", DateTime.Now.ToString(), "保持连接状态");
}
else
{
label1.Text
=string.Format("时间:{0},{1}", DateTime.Now.ToString(), "断开,需要重启");
}
}
});
tClient.IsBackground
=true;
tClient.Start();
}
privatevoid buttonSet_Click(object sender, EventArgs e)
{
//模拟发送心跳数据
autoResetEvent.Set();
}
复制代码


备注:由本问题带来一个Winform跨线程控件赋值和操作的问题。由于在本示例中不影响上面代码的运行,所以没有涉及,但是回复中有人提出来,所以提前简述一下Winform的线程模型:

在Winform框架中,有一个ISynchronizeInvoke接口,所有的UI元素(表现为Control)都继承了该接口。其中,接口中的InvokdRequired属性表示了当前线程是否是创建它的线程。接口中的Invoke和BeginInvoke方法负责将消息发送到消息队列中,这样,UI线程就能够正确处理它。

具体到代码中,对于夸线程控件赋值,可以采用下面的方法:

this.label1.BeginInvoke(new Action(()=>
{
this.label1.Text ="跨线程中赋值";
}));

免责声明:文章转载自《信号同步》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇Linux(CentOS6)上安装python3.6 并创建虚拟环境 -----最牛逼的方法usb2.0高速视频采集之68013A寄存器配置说明下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

你知道 Java 代码是如何运行的吗?

对于任何一门语言,要想达到精通的水平,研究它的执行原理(或者叫底层机制)不失为一种良好的方式。 在本篇文章中,将重点研究java源代码的执行原理,即从程序员编写JAVA源代码,到最终形成产品,在整个过程中,都经历了什么?每一步又是怎么执行的?执行原理又是什么? 一 编写java源程序 java源文件:指存储java源码的文件。 先来看看如下代码: (1...

JVM基本讲解

 1.数据类型     java虚拟机中,数据类型可以分为两类:基本类型和引用类型。     基本类型的变量保存原始值,即:它代表的值就是数值本身,而引用类型的变量保存引用值。     “引用值”代表了某个对象的引用,而不是对象本身,对象本身存放在这个引用值所表示的地址的位置。     基本类型包括:byte、short、int、long、char、flo...

Android 异步加载解决方案

Android的Lazy Load主要体现在网络数据(图片)异步加载、数据库查询、复杂业务逻辑处理以及费时任务操作导致的异步处理等方面。在介绍Android开发过程中,异步处理这个常见的技术问题之前,我们简单回顾下Android开发过程中需要注意的几个地方。 Android应用开发过程中必须遵循单线程模型(Single Thread Model)的原则。因...

C# 多线程(lock,Monitor,Mutex,同步事件和等待句柄)

本篇从 Monitor,Mutex,ManualResetEvent,AutoResetEvent,WaitHandler 的类关系图开始,希望通过本篇的介绍能对常见的线程同步方法有一个整体的认识,而对每种方式的使用细节,适用场合不会过多解释。 让我们来看看这几个类的关系图: 1. lock 关键字 lock 是 C# 关键词,它将语句块标记为临界区,...

【性能测试】:JVM内存监控策略的方法,以及监控结果说明

JVM内存监控主要在稳定性压测期间,监控应用服务器内存泄露等问题; 【JVM远程监控设置】 1、打开WAS控制台:https://ip:port/ibm/console/login.do 2、进入路径:应用程序服务器 > Server_Name > 进程定义> Java 虚拟机 3、在通用JVM参数中开启远程访问,并设置远程访问端口为10...

WPF 同一窗口内的多线程 UI(VisualTarget)

WPF 的 UI 逻辑只在同一个线程中,这是学习 WPF 开发中大家几乎都会学习到的经验。如果希望做不同线程的 UI,大家也会想到使用另一个窗口来实现,让每个窗口拥有自己的 UI 线程。然而,就不能让同一个窗口内部使用多个 UI 线程吗? 答案其实是——可以的!使用 VisualTarget 即可。 阅读本文将收获一份对 VisualTarget 的解读以...