线程同步之事件

  我们可以使用lock、Mutex来保证共享资源被正确的操作,但当多个线程之间需要相互通信时,如线程A完成之后要告诉线程B,B在接着做,

这时我们应该怎么处理,那就要用到线程的事件。.Net中提供了AutoResetEvent和ManualResetEvent两个类来处理。

  当线程需要独占资源时,使用AutoResetEvent.WaitOne()来等待资源,如果AutoResetEvent为非终止状态,则线程阻塞,等待其它线程释放资源。其它线程

使用完资源,通过调用Set方法来释放资源,通知等待线程资源可用。第一个等待的线程接到信号之后即可占用资源继续执行,当线程执行完毕,会自动将AutoResetEvent设置

为终止状态。

AutoResetEvent 在初始化时通过参数来设置初始化状态,true表示终止状态,false表示非终止状态。

我们来看一个例子。

分两个人完成植树,A负责挖树坑,B负责搬树苗及浇水。代码实现如下:

public class ThreadEventTest
    {
        private static AutoResetEvent autoResetEvent = new AutoResetEvent(false);
        public static void PlantTree()
        {
            //挖坑
            Thread shovelThread = new Thread(new ThreadStart(Shovel));
            shovelThread.Name = "A";
            shovelThread.Start();
            Thread.Sleep(100);
            //种树浇水
            Thread warterThread = new Thread(new ThreadStart(WarterTree));
            warterThread.Name = "B";
            warterThread.Start();            
        }

        private static void Shovel()
        {            
            Console.WriteLine("线程{0}开始挖坑",Thread.CurrentThread.Name);            
            Console.WriteLine("线程{0}挖坑完成,可以种树浇水了", Thread.CurrentThread.Name);
            autoResetEvent.Set();
        }

        private static void WarterTree()
        {            
            Console.WriteLine("线程{0}开始搬运树苗并准备水", Thread.CurrentThread.Name);                        
            Console.WriteLine("线程{0}已将树苗和水准备就绪,等待挖坑完成后可以种树浇水了", Thread.CurrentThread.Name);
            autoResetEvent.WaitOne();

            Console.WriteLine("线程{0}开始种树浇水", Thread.CurrentThread.Name);
            Thread.Sleep(1000);
            Console.WriteLine("线程{0}种树浇水完成", Thread.CurrentThread.Name);
            //
            Console.WriteLine("完成植树");
        }
    }

主线程开启两个线程A、B分别完成挖坑和浇水。A线程不受限制开启后会执行挖坑任务,此时B线程也会同步执行,但当B线程执行值WaitOne时,由于我们初始化的AutoResetEvent状态为非终止状态,因此B线程必须等待Set信号,只有当A线程执行完毕并进行Set时,即A线程挖坑完成,B线程才能进行植树浇水,从而保证了整个

植树工作有序的完成。

AutoResetEvent在Set之后状态变为终止状态,当WaitOne执行之后又会自动将状态重置为非终止状态,而无需调用Reset(),这就是和ManualResetEvent之间的区别。

ManualResetEvent在Set之后必须要Reset才能重置状态,调用需要成对出现,才能保证WaitOne阻塞线程,使得线程有序的执行下去。

原文地址:https://www.cnblogs.com/oneheart/p/5636359.html