ManualResetEvent 和 AutoResetEvent 快速扫盲

本文不打算详细介绍这两个东东,只是简单给个示例说明下两者的区别。


背 景

在多线程的环境下,我们有时候希望某个线程在其它线程之后再继续执行,比如在操作一个文本的时候,一个线程执行删除,另一个线程执行写入,这时候就需要通过某种方法让这两个线程按照次序执行。这也就是所谓的线程同步,这个 “同” 字应该理解为协同、合作。而信号量就是其中一种线程同步的方式,就好像红绿灯一样,当信号为红灯时,我们知道要停止,变成绿灯时,就可以通行了。所有采用信号量的类都是基于 WaitHandle 这个类的实现。


我们的主角 ManualResetEvent 和 AutoResetEvent 继承自 EventWaitHandle, 它正是实现了 WaitHandler。EventWaitHandle 提供了 Set(释放信号量)、Reset(终止信号量)方法用于进行线程同步。下面,我就提供两个简单的示例来演示一下如何操作。

ManualResetEvent 和 AutoResetEvent


WaitHandle 实际使用的是 Windows 的内核对象,当我们使用该对象的时候需要从用户模式切换到内核模式,这会非常消耗性能。参考谈谈用内核对象进行线程同步

区别演示

在进入正题前,先来看一段简单的动画演示,快速了解下这两者的区别。

ManualResetEvent 效果


AutoResetEvent 效果


ManualResetEvent 

Manual 的中文意思是 “手动”,也就是这是一个需要手动终止的事件,换句话说,如果不手动终止,那么信号一直为释放状态,相当于一直是绿灯。

调用 Set 表示释放信号,之后只有当调用了 Reset 方法后,信号才能重新成为终止状态。

    private ManualResetEvent mre = new ManualResetEvent(false); //初始时让信号为终止状态
 
    public void Suspend()
    {
        mre.Reset();
    }
 
    public void Resume()
    {
        mre.Set();
    }
 
    public void CreateThreadsAndRun()
    {
        Thread t = new Thread(Run);
        t.Start();
    }
 
    private void Run(object obj)
    {
        while (true)
        {
            mre.WaitOne(); //如果信号为终止状态,那么这句话将会阻塞当前线程,直到收到信号释放的状态
            mre.WaitOne(); //当信号为释放状态时,所有 WaitOne 都不会阻塞。
 
            Console.WriteLine(Thread.CurrentThread.ManagedThreadId + " is running...");
            Thread.Sleep(2000);
        }
    }


AutoResetEvent 

Auto 的中文意思是 “自动”,和 Manual 正好相反。所以大家应该已经猜到这两者的区别了,当信号为释放状态,只要有线程调用了一次 WaitOne ,那么信号就变成 “终止” 状态。

    private AutoResetEvent are = new AutoResetEvent(true); //初始为 释放 状态
 
    public void Suspend()
    {
        are.Reset();
    }
 
    public void Resume()
    {
        are.Set();
    }
 
    public void CreateThreadsAndRun()
    {
        Thread t = new Thread(Run);
        t.Start();
    }
 
    private void Run(object obj)
    {
        while (true)
        {
            are.WaitOne();
            Console.WriteLine(Thread.CurrentThread.ManagedThreadId + " is running..."); //第一次因为是 释放 状态,该句被打印在屏幕上
            
            are.WaitOne(); //因为已经调用过一次 WaitOne ,所以第二次调用 WaitOne 时,信号已经变成 终止 状态。该句将阻塞当前线程。
            Console.WriteLine(Thread.CurrentThread.ManagedThreadId + " is running..."); 
            Thread.Sleep(2000);
        }
    }


文章索引

[隐 藏]

本站采用知识共享署名 3.0 中国大陆许可协议进行许可。 ©2014 Charley Box | 关于本站 | 浙ICP备13014059号