使用EventWaitHandle控制线程间的同步

 EventWaitHandle 提供了自动(EventResetMode.AutoReset)和手动(EventResetMode.ManualReset)两种方式,其主要区别是否自动将同步状态设置为非终止状态,以阻塞线程。
static void EventWaitHandleTest2(EventResetMode mode)
{
  EventWaitHandle r1 = new EventWaitHandle(false, mode);

  Thread t1 = new Thread(delegate()
    {
      while (true)
      {
        r1.WaitOne();

        Console.WriteLine("1");
        Thread.Sleep(1000);

        //r1.Reset();
        //r1.Set();
      }
    }
  );

  t1.Start();
  r1.Set();
}

当我们使用 EventWaitHandleTest2(EventResetMode.AutoReset); 调用该方法时,我们发现循环体在执行一次后并没有继续,线程被阻塞。而 EventWaitHandleTest2(EventResetMode.ManualReset); 调用时则不会阻塞,循环体无限执行下去。由此我们可以区别这两种状态的不同,自动方式会自动将同步状态设置为非终止,而手动则不会。自动状态时,我们删除 r1.Set(); 行的注释字符 (//),将状态重置为终止状态,则循环体自动进行下一轮执行;手动方式,我们将 r1.Reset(); 行的注释行删除,那么同步状态会被重置为非终止状态状态,那么线程体自然被阻塞,不会继续循环体的执行了。

.net Framework 2.0 的 AutoResetEvent 和 ManualResetEvent 继承自 EventWaitHandle,而不是 1.x 中继承自 WaitHandle。这两个类只是重写了构造方法而已。
public AutoResetEvent(bool initialState) : base(initialState, EventResetMode.AutoReset)
{
}

public ManualResetEvent(bool initialState) : base(initialState, EventResetMode.ManualReset)
{
}

接下来,我们另写一个例子。让两个线程交替执行。
using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
using System.Threading;

namespace ConsoleApplication1
{
  class Program
  {
    static void EventWaitHandleTest(EventResetMode mode)
    {
      EventWaitHandle r1 = new EventWaitHandle(false, mode);
      EventWaitHandle r2 = new EventWaitHandle(false, mode);

      Thread t1 = new Thread(delegate()
        {
          while (true)
          {
            r1.WaitOne();

            Console.WriteLine("1");
            Thread.Sleep(1000);

            if (mode == EventResetMode.ManualReset) r1.Reset();
            r2.Set();
          }
        }
      );

      Thread t2 = new Thread(delegate()
        {
          while (true)
          {
            r2.WaitOne();
            Console.WriteLine("2");

            if (mode == EventResetMode.ManualReset) r2.Reset();
            r1.Set();
          }
        }
      );

      t1.Start();
      t2.Start();

      r1.Set();
    }

    static void Main()
    {
      EventWaitHandleTest(EventResetMode.AutoReset);

      Console.WriteLine("Press any key to exit...");
      Console.ReadKey(true);
    }
  }
}


我们为什么要用2个同步对象呢?试想如果用一个的话在执行完 t1 的循环体 r1.Set(); 代码时,CPU 分配给 t1 的时间片断可能还没有超时,那么自然不会把执行权交给 t2,因为会继续执行 t1 的循环体代码,因此用 2 个则可以避免这种问题。

==============================================================

不是线程的非终止状态是事件的非终止状态,
AutoResetEvent形象说就像一个水龙头一样,当你关闭水龙头,状态就是关闭(终止状态),所有的水都在里面等待流出来,当水龙头打开,就发送一个事件说里面的水可以出来了,这时就是非终止状态,一般是用来同步访问资源

AutoResetEvent类的对象,如ManualResetEvent对象一样,大家可以把它简单地理解为一个信号灯,使用AutoResetEvent.Set()方法可以设置它为有信号状态,而使用AutoResetEvent.Reset()方法把它设置为无信号状态。这里用它的有信号状态来表示一个线程的结束。
以一个例子(以线程)来说:
控制多个线程相互之间的联系呢?例如我要到餐厅吃饭,在吃饭之前我先得等待厨师把饭菜做好,之后我开始吃饭,吃完我还得付款,付款方式可以是现金,也可以是信用卡,付款之后我才能离开。分析一下这个过程,我吃饭可以看作是主线程,厨师做饭又是一个线程,服务员用信用卡收款和收现金可以看作另外两个线程,大家可以很清楚地看到其中的关系——我吃饭必须等待厨师做饭,然后等待两个收款线程之中任意一个的完成,然后我吃饭这个线程可以执行离开这个步骤,于是我吃饭才算结束了。事实上,现实中有着比这更复杂的联系,我们怎样才能很好地控制它们而不产生冲突和重复呢?

这种情况下,我们需要用到互斥对象,即System.Threading命名空间中的Mutex类。大家一定坐过出租车吧,事实上我们可以把Mutex看作一个出租车,那么乘客就是线程了,乘客首先得等车,然后上车,最后下车,当一个乘客在车上时,其他乘客就只有等他下车以后才可以上车。而线程与Mutex对象的关系也正是如此,线程使用Mutex.WaitOne()方法等待Mutex对象被释放,如果它等待的Mutex对象被释放了,它就自动拥有这个对象,直到它调用Mutex.ReleaseMutex()方法释放这个对象,而在此期间,其他想要获取这个Mutex对象的线程都只有等待。

下面这个例子使用了Mutex对象来同步四个线程,主线程等待四个线程的结束,而这四个线程的运行又是与两个Mutex对象相关联的。其中还用到AutoResetEvent类的对象,如同上面提到的ManualResetEvent对象一样,大家可以把它简单地理解为一个信号灯,使用AutoResetEvent.Set()方法可以设置它为有信号状态,而使用AutoResetEvent.Reset()方法把它设置为无信号状态。这里用它的有信号状态来表示一个线程的结束。

// Mutex.cs
using System;
using System.Threading;

public class MutexSample
{
  static Mutex gM1;
  static Mutex gM2;
  const int ITERS = 100;
  static AutoResetEvent Event1 = new AutoResetEvent(false);
  static AutoResetEvent Event2 = new AutoResetEvent(false);
  static AutoResetEvent Event3 = new AutoResetEvent(false);
  static AutoResetEvent Event4 = new AutoResetEvent(false);

  public static void Main(String[] args)
  {
  Console.WriteLine("Mutex Sample ...");
  //创建一个Mutex对象,并且命名为MyMutex
  gM1 = new Mutex(true,"MyMutex");
  //创建一个未命名的Mutex 对象.
  gM2 = new Mutex(true);
  Console.WriteLine(" - Main Owns gM1 and gM2");

  AutoResetEvent[] evs = new AutoResetEvent[4];
  evs[0] = Event1; file://为后面的线程t1,t2,t3,t4定义AutoResetEvent对象
  evs[1] = Event2;
  evs[2] = Event3;
  evs[3] = Event4;

  MutexSample tm = new MutexSample( );
  Thread t1 = new Thread(new ThreadStart(tm.t1Start));
  Thread t2 = new Thread(new ThreadStart(tm.t2Start));
  Thread t3 = new Thread(new ThreadStart(tm.t3Start));
  Thread t4 = new Thread(new ThreadStart(tm.t4Start));
  t1.Start( );// 使用Mutex.WaitAll()方法等待一个Mutex数组中的对象全部被释放
  t2.Start( );// 使用Mutex.WaitOne()方法等待gM1的释放
  t3.Start( );// 使用Mutex.WaitAny()方法等待一个Mutex数组中任意一个对象被释放
  t4.Start( );// 使用Mutex.WaitOne()方法等待gM2的释放


  Thread.Sleep(2000);
  Console.WriteLine(" - Main releases gM1");
  gM1.ReleaseMutex( ); file://线程t2,t3结束条件满足

  Thread.Sleep(1000);
  Console.WriteLine(" - Main releases gM2");
  gM2.ReleaseMutex( ); file://线程t1,t4结束条件满足

  //等待所有四个线程结束
  WaitHandle.WaitAll(evs);
  Console.WriteLine("... Mutex Sample");
  Console.ReadLine();
  }

  public void t1Start( )
  {
  Console.WriteLine("t1Start started, Mutex.WaitAll(Mutex[])");
  Mutex[] gMs = new Mutex[2];
  gMs[0] = gM1;//创建一个Mutex数组作为Mutex.WaitAll()方法的参数
  gMs[1] = gM2;
  Mutex.WaitAll(gMs);//等待gM1和gM2都被释放
  Thread.Sleep(2000);
  Console.WriteLine("t1Start finished, Mutex.WaitAll(Mutex[]) satisfied");
  Event1.Set( ); file://线程结束,将Event1设置为有信号状态
  }

  public void t2Start( )
  {
  Console.WriteLine("t2Start started, gM1.WaitOne( )");
  gM1.WaitOne( );//等待gM1的释放
  Console.WriteLine("t2Start finished, gM1.WaitOne( ) satisfied");
  Event2.Set( );//线程结束,将Event2设置为有信号状态
  }

  public void t3Start( )
  {
  Console.WriteLine("t3Start started, Mutex.WaitAny(Mutex[])");
  Mutex[] gMs = new Mutex[2];
  gMs[0] = gM1;//创建一个Mutex数组作为Mutex.WaitAny()方法的参数
  gMs[1] = gM2;
  Mutex.WaitAny(gMs);//等待数组中任意一个Mutex对象被释放
  Console.WriteLine("t3Start finished, Mutex.WaitAny(Mutex[])");
  Event3.Set( );//线程结束,将Event3设置为有信号状态
  }

  public void t4Start( )
  {
  Console.WriteLine("t4Start started, gM2.WaitOne( )");
  gM2.WaitOne( );//等待gM2被释放
  Console.WriteLine("t4Start finished, gM2.WaitOne( )");
  Event4.Set( );//线程结束,将Event4设置为有信号状态
  }
}

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值