前言


在我们编写多线程程序时,会遇到这样一个问题:在一个线程处理的过程中,需要等待另一个线程处理的结果才能继续往下执行。比如:有两个线程,一个用来接收Socket数据,另一个用来处理Socket数据,而处理Socket数据的那个线程需要在接收到Socket数据后才能处理运行,就要等待接收线程接收数据。那么处理线程如何等待,接收线程又如何通知处理线程呢?

其中一个比较好的方式就是使用AutoResetEvent/ManualResetEvent

1. AutoResetEvent/ManualResetEvent介绍


AutoResetEvent/ManualResetEvent是.Net给我们提供的用于线程间同步的对象。都有三个主要的函数:

WaitOne: 用于阻塞线程,等待接收到继续运行信号

Set:          用于发送同步信号,通知正在等待的线程继续运行

Reset:      重置终止状态

初始化构造函数ResetEvent(bool initialState),当initialState为true时,默认为终止状态(即阻塞无效);当initialState为false时,默认为非终止状态

关于ResetEvent的终止状态与非终止状态,大家刚开始看的时候会感到难以理解,我举个很形象的例子,大家就会明白了。

我们都有上班刷卡进大楼的经历,这里可以把ResetEvent看作是那个闸门,只有刷卡才能进。

WaitOne就是等待刷卡,Set就是刷卡,而Reset则是关闭闸门,这样就知道,当初始化的时候,initialState为true时,意思就是说,默认闸门是开的;initialState为false时,默认闸门是关的

所以,如果要进入大楼,步骤就应该是这样的(假设当前闸门是关着的),

等待刷卡(WaitOne) ->刷卡(Set)->通信->关闭闸门(Reset)

2. AutoResetEvent/ManualResetEvent的区别


其实,这两个类的字面上的意思已经告诉了我们他们间的区别,Reset既然是关闭闸门,那也就是说,Auto是自动关闭闸门,Manual是手动关闭闸门。

AutoResetEvent:刷卡通过后,闸门自动关闭,然后等待下一次刷卡

ManualResetEvent:刷卡通过后,闸门不会自动关闭,如果不手动关闭(调用Reset方法),等待刷卡无效,人也就不用刷卡就能直接通过

所以WaitOne是否有效,能不能阻塞线程取决于是否调用了Reset方法。

而构造函数中initialState为true时,表示闸门默认第一次是打开的

3. AutoResetEvent/ManualResetEvent使用示例


接下来,我们用一个简单的示例来看一下这两个类的实际效果。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading; namespace ResetEventTest
{
class Program
{
     //初始化时默认为非终止状态false
static AutoResetEvent autoRE = new AutoResetEvent(false);
static ManualResetEvent manualRE = new ManualResetEvent(false); static void Main(string[] args)
{
(new Thread(AutoMethod1)).Start();
(new Thread(AutoMethod2)).Start(); (new Thread(ManualMethod1)).Start();
(new Thread(ManualMethod2)).Start(); Console.ReadKey();
} static void AutoMethod1()
{
Console.WriteLine("wait print AutoMethod 1:");
autoRE.WaitOne();
Console.WriteLine("AutoMethod 1");
} static void AutoMethod2()
{
Console.WriteLine("wait print AutoMethod 2:");
autoRE.WaitOne();
Console.WriteLine("AutoMethod 2");
} static void ManualMethod1()
{
Console.WriteLine("wait print ManualMethod 1:");
manualRE.WaitOne();
Console.WriteLine("ManualMethod 1");
} static void ManualMethod2()
{
Console.WriteLine("wait print ManualMethod 2:");
manualRE.WaitOne();
Console.WriteLine("ManualMethod 2");
}
}
}

分别对应AutoResetEvent和ManualResetEvent启动两个线程,全部进入等待,这时候输出为:

我们发现线程全部阻塞,这说明初始化时false,就是设置为阻塞状态,那我们把初始化参数改为true试下

static AutoResetEvent autoRE = new AutoResetEvent(true);
static ManualResetEvent manualRE = new ManualResetEvent(true);

结果如下:

发现(注意输出顺序可能不同),Auto仅阻塞了一个,而Manual则两个都没有阻塞,全部输出了,实际上即是Auto放行一个后,立即自动调用了Reset方法,导致AutoMethod2在没有获取Set通行信号前无法继续运行;而Manual因为没有手动调用Reset方法,WaitOne形同虚设没有起任何作用。

还使用之前的代码,在启动线程的时候添加Set代码,如下:

static AutoResetEvent autoRE = new AutoResetEvent(false);
static ManualResetEvent manualRE = new ManualResetEvent(false); static void Main(string[] args)
{
(new Thread(AutoMethod1)).Start();
autoRE.Set();
(new Thread(AutoMethod2)).Start(); (new Thread(ManualMethod1)).Start();
manualRE.Set();
(new Thread(ManualMethod2)).Start(); Console.ReadKey();
}

当AutoMethod1线程启动后,调用了Set方法,那么AutoMethod1线程就获得了继续运行的信号,这时候启动AutoMethod2线程,因为AutoMethod1线程运行后自动重置了状态,所以AutoMethod2就会阻塞;而ManualMethod1线程之后调用Set,因为没有手动调用Reset重置状态,那么ManualMethod2将不会阻塞,如下结果验证了我们的想法:

如果想要ManualMethod2阻塞,那么只要在ManualMethod2中,获取到Set信号后,再调用Reset方法即可

static void ManualMethod1()
{
Console.WriteLine("wait print ManualMethod 1:");
manualRE.WaitOne();
manualRE.Reset();
Console.WriteLine("ManualMethod 1");
}

结果:

4. 其他操作


其实我们查看ResetEvent的继承关系会发现,实际上ResetEvent继承自抽象类WaitHandle,WaitHandle下的SignalAndWait,WaitAll与WaitAny方法提供了我们更多样的控制线程同步的方式,这个在以后会进一步探讨。

线程同步(AutoResetEvent与ManualResetEvent)的更多相关文章

  1. 线程同步 –AutoResetEvent和ManualResetEvent

    上一篇介绍了通过lock关键字和Monitor类型进行线程同步,本篇中就介绍一下通过同步句柄进行线程同步. 在Windows系统中,可以使用内核对象进行线程同步,内核对象由系统创建并维护.内核对象为内 ...

  2. C# 应用 - 多线程 6) 处理同步数据之手动同步 AutoResetEvent 和 ManualResetEvent

    1. 类的关系 AutoResetEvent 和 ManualResetEvent 都继承自 System.Threading.EventWaitHandle 类(EventWaitHandle 继承 ...

  3. 线程中AutoResetEvent与ManualResetEvent的区别

    线程之间的通信是通过发信号来进行沟通的.. ManualResetEvent发送Set信后后,需要手动Reset恢复初始状态.而对于AutoResetEvent来说,当发送完毕Set信号后,会自动Re ...

  4. 多线程同步AutoResetEvent 和ManualResetEvent

  5. 重新想象 Windows 8 Store Apps (47) - 多线程之线程同步: Semaphore, CountdownEvent, Barrier, ManualResetEvent, AutoResetEvent

    [源码下载] 重新想象 Windows 8 Store Apps (47) - 多线程之线程同步: Semaphore, CountdownEvent, Barrier, ManualResetEve ...

  6. 基元线程同步构造 AutoResetEvent和ManualResetEvent 线程同步

    在.Net多线程编程中,AutoResetEvent和ManualResetEvent这两个类经常用到, 他们的用法很类似,但也有区别.ManualResetEvent和AutoResetEvent都 ...

  7. C#多线程同步事件及等待句柄AutoResetEvent 和 ManualResetEvent

    最近捣鼓了一下多线程的同步问题,发现其实C#关于多线程同步事件处理还是很灵活,这里主要写一下,自己测试的一些代码,涉及到了AutoResetEvent 和 ManualResetEvent,当然还有也 ...

  8. C# 使用ManualResetEvent 进行线程同步

    上一篇我们介绍了AutoResetEvent,这一篇我们来看下ManualResetEvent ,顾名思义ManualResetEvent  为手动重置事件. AutoResetEvent和Manua ...

  9. C# 线程间互相通信 AutoResetEvent和ManualResetEvent

    C#线程间互相通信主要用到两个类:AutoResetEvent和ManualResetEvent. 一.AutoResetEvent AutoResetEvent 允许线程通过发信号互相通信,线程通过 ...

随机推荐

  1. UVA 586 Instant Complexity

    给出一段程序,求运行时间. 现在只考虑一层LOOP,不妨用数组a[i]来表示n的i次方的系数.如果输入OP m,那么就在a[0]上加m,遇到END,就说明循环结束了,需要在系数上乘以循环次数.如果次数 ...

  2. cmd命令行设置环境变量

    http://blog.sciencenet.cn/blog-51026-566742.html 1.查看当前所有可用的环境变量:输入 set 即可查看. 2.查看某个环境变量:输入 “set 变量名 ...

  3. 新学Linux时遇到的一些问题

    1.如何进入到Linux的命令终端 (1)按ctrl+alt+t (2)按ctrl+alt+(F1-F6都可以),按ctrl+alt+F7退出 2.按ctrl+alt+(F1-F6)进入命令终端,登录 ...

  4. 265行JavaScript代码的第一人称3D H5游戏Demo【个人总结1】

    本文目的是分解前面的代码.其实,它得逻辑很清楚,只是对于我这种只是用过 Canvas 画线(用过 Fabric.js Canvas库)的人来说,这个还是很复杂的.我研究这个背景天空也是搞了一天,下面就 ...

  5. ZOJ Problem Set - 3758 素数

    Singles' Day Time Limit: 2 Seconds Memory Limit: 65536 KB Singles' Day(or One's Day), an unofficial ...

  6. freepbx 安装和配置

    首先参见   asterisk manager api 的配置,然后根据freepbx的官方文档: http://wiki.freepbx.org/display/HTGS/Installing+Fr ...

  7. [CF 351B]Jeff and Furik[归并排序求逆序数]

    题意: 两人游戏, J先走. 给出一个1~n的排列, J选择一对相邻数[题意!!~囧], 交换. F接着走, 扔一硬币, 若正面朝上, 随机选择一对降序排列的相邻数, 交换. 若反面朝上, 随机选择一 ...

  8. Parallel.Foreach的并发问题解决方法-比如爬虫WebClient

    场景五:线程局部变量 Parallel.ForEach 提供了一个线程局部变量的重载,定义如下: public static ParallelLoopResult ForEach<TSource ...

  9. sqlserver 遇到以零作除数错误的处理 不报错的解决方法

    使用sqlserver 的选项来禁止出现以零除的错误中断,让而让其为null set ansi_warnings offSET ARITHABORT offSET ARITHIGNORE on sel ...

  10. vs 点击就设置项目为默认启动项

    装好系统没注意 做项目的时候解决方案比较多 发现点击哪个项目哪个项目就成了默认启动项目 这个开始没觉出来 最后发现挺烦人的 想想难道是我装vs装的么 我于是卸载了全新安装了 还是一个吊样 无意间试了下 ...