【多线程笔记】ManualResetEvent、AutoResetEvent

信号

除了锁之外,.Net还提供了线程间更自由通讯工具,它们提供了通过“信号”通讯的机制。有时候你需要让线程处于等待状态,直到接收其他线程发来的消息,这就叫发送信号
最简单的发送信号的方式就是使用ManualResetEventManualResetEvent。调用它的WaitOne()方法阻塞线程,调用Set()方法开启信号,通俗的比喻为“开门” set()、“关门” Reset()、"等着开门" WaitOne()

WaitHandler

WaitHandle是一个抽象类,不能实例化。

下面看看WaitHandle中常用方法:

实例方法:
WaitOne():阻止当前线程,直到当前 WaitHandle 收到信号
WaitOne(Int32):阻止当前线程,直到当前 WaitHandle 收到信号,同时使用 32 位带符号整数表示超时时间
静态方法:
WaitHandle.WaitAll(WaitHandle[]):等待指定数组中的所有元素都收到信号
WaitHandle.WaitAll(WaitHandle[], Int32):等待指定数组中的所有元素接收信号,同时使用 32 位带符号整数表示超时时间
WaitHandle.WaitAny(WaitHandle[]):等待指定数组中的任一元素收到信号
WaitHandle.WaitAny(WaitHandle[], Int32):等待指定数组中的任意元素接收信号,同时使用 32 位带符号整数表示超时时间

同步事件EventWaitHandle

EventWaitHandle 类允许线程通过发信号互相通信, 通常情况下,一个或多个线程在 EventWaitHandle 上阻止,直到一个未阻止的线程调用 Set 方法,以释放一个或多个被阻止的线程。
在EventWaitHandle类型中,除了父类中的方法,又有自己的特有方法,下面几个是比较常用的:

实例方法:
Set:将事件状态设置为终止状态,允许一个或多个等待线程继续
Reset:将事件状态设置为非终止状态,导致线程阻止
静态方法:
OpenExisting(String):打开指定名称为同步事件(如果已经存在);通过一个命名的EventWaitHandle我们可以进行进程之间的线程同步,未命名的EventWaitHandle只能进行本进程中的线程同步
EventWaitHandle类型有两个子类AutoResetEvent和ManualResetEvent,这两个子类分别代表了EventWaitHandle类型对事件状态的重置模式。在释放单个等待线程后,用 EventResetMode.AutoReset 标志创建的 EventWaitHandle 在终止时会自动重置; 用 EventResetMode.ManualReset 标志创建的 EventWaitHandle 一直保持终止状态,直到它的 Reset 方法被调用。

通过EventWaitHandle类型的构造函数,我们可以通过参数指定EventResetMode,从而选择事件状态的重置模式。当然,我们也可以直接使用EventWaitHandle的两个子类。

public EventWaitHandle(bool initialState, EventResetMode mode);

public enum EventResetMode
{
    AutoReset = 0,
    ManualReset = 1,
}

ManualResetEvent

ManualResetEvent 是一旦设定 Set()后就一直开门,除非调用 Reset() 关门。

ManualResetEvent mre = new ManualResetEvent(false);
//构造函数 false 表示“初始状态为关门”,设置为 true 则初始化为开门状态
Thread t1 = new Thread(()=> {
Console.WriteLine("开始等着开门");
mre.WaitOne();
Console.WriteLine("终于等到你");
});
t1.Start();
Console.WriteLine("按任意键开门");
Console.ReadKey();
mre.Set();//开门(打开信号)

WaitOne()还可以设置等待超时时间:

ManualResetEvent mre = new ManualResetEvent(false);
//false 表示“初始状态为关门”
Thread t1 = new Thread(()=> {
Console.WriteLine("开始等着开门");
if(mre.WaitOne(5000))
{
Console.WriteLine("终于等到你");
}
else
{
Console.WriteLine("等了 5 秒钟都没等到");
}
});
t1.Start();
Console.WriteLine("按任意键开门");
Console.ReadKey();
mre.Set();//开门(打开信号)

AutoResetEvent

他是在开门并且一个 WaitOne 通过后自动关门,因此命名为“AutoResetEvent”(Auto:自动、Reset:关门)
线程通过调用 AutoResetEvent 上的 WaitOne 来等待信号。如果 AutoResetEvent 处于非终止状态,则该线程阻塞,并等待当前控制资源的线程
调用 SetAutoResetEvent 发信号以释放等待线程。AutoResetEvent 将保持终止状态,直到一个正在等待的线程被释放,然后自动返回非终止状态。如果没有任何线程在等待,则状态将无限期地保持为终止状态。
下面例子有三个线程,使用事件锁来实现同步执行:

static void Main(string[] args)
        {
            var t1ResetEvent = new AutoResetEvent(false);
            var t2ResetEvent = new AutoResetEvent(false);
            var t3ResetEvent = new AutoResetEvent(false);
            Task t1 = new Task(() =>
            {
                while (true)
                {
                    if (t1ResetEvent.WaitOne())
                    {
                        Console.WriteLine("t1");
                        Thread.Sleep(500);
                        t2ResetEvent.Set();
                    }
                }
            });
            Task t2 = new Task(() =>
            {
                while (true)
                {
                    if (t2ResetEvent.WaitOne())
                    {
                        Console.WriteLine("t2");
                        Thread.Sleep(500);
                        t3ResetEvent.Set();
                    }
                }
            });
            Task t3 = new Task(() =>
            {
                while (true)
                {
                    if (t3ResetEvent.WaitOne())
                    {
                        Console.WriteLine("t3");
                        Thread.Sleep(500);
                    }
                }
            });
            t1.Start();
            t2.Start();
            t3.Start();
            t1ResetEvent.Set();
            Thread.Sleep(3000);
            t1ResetEvent.Set();
            Thread.Sleep(3000);
            t1ResetEvent.Set();
            Console.ReadKey();
        }

//结果 t1、t2、t3、t1、t2、t3、t1、t2、t3

Mutex

Mutex和Monitor的区别
这两者虽然都用来进行同步的功能,但实现方法不同,其最显著的两个差别如下:
Mutex使用的是操作系统的内核对象,而Monitor类型的同步机制则完全在.NET框架之下实现,这就导致了Mutext类型的效率要比Monitor类型要低很多;
Monitor类型只能同步同一应用程序域中的线程,而Mutex类型却可以跨越应用程序域和进程。

static Mutex mutex = new Mutex();
{
mutex.WaitOne();
Console.WriteLine(num2++);
mutex.ReleaseMutex();
}
posted @ 2020-08-12 21:38  .Neterr  阅读(203)  评论(0)    收藏  举报