打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
ManualResetEvent和AutoResetEvent的区别

     ManualResetEvent都可以阻塞一个或多个线程,直到收到一个信号告诉ManualResetEvent不要再阻塞当前的线程。

可以想象ManualResetEvent这个对象内部有一个Boolean类型的属性IsRelease来控制是否要阻塞当前线程。这个属性我们在初始化的时候可以设置它,如ManualResetEvent event=new ManualResetEvent(false);这就表明默认的属性是要阻塞当前线程。

      代码举例:

        ManualResetEvent _manualResetEvent = new ManualResetEvent(false);

        private void BT_Temp_Click(object sender, RoutedEventArgs e)
        {
            Thread t1 = new Thread(this.Thread1Foo);
            t1.Start(); //启动线程1
            Thread t2 = new Thread(this.Thread2Foo);
            t2.Start(); //启动线程2
            Thread.Sleep(3000); //睡眠当前主线程,即调用BT_Temp_Click的线程
            _manualResetEvent .Set();   //想象成将IsRelease设为True 
        }

        void Thread1Foo()
        {
            _manualResetEvent .WaitOne(); 

//阻塞线程1,直到主线程发信号给线程1,告知_menuResetEvent你的IsRelease属性已经为true,

//这时不再阻塞线程1,程序继续往下跑

             MessageBox.Show("t1 end");
        }

        void Thread2Foo()
        {
            _manualResetEvent .WaitOne();

//阻塞线程2,直到主线程发信号给线程1,告知_menuResetEvent你的IsRelease属性已经为true,

//这时不再阻塞线程2,程序继续往下跑

            MessageBox.Show("t2 end");
        }

 

       注意这里ManualResetEvent和AutoResetEvent的一个重要区别:

       manual的话肯定会给线程1和线程2都发送一个信号,而auto只会随机给其中一个发送信号。

 

 

       为什么一个叫manual而一个叫auto呢?我想这是很多人的疑问,现在我们就来看这个问题。

       刚才_manualResetEvent .Set();的这句话我想大家都明白了,可以看做将IsRelease的属性设置为true.线程1中

 _manualResetEvent.WaitOne();接收到信号后不再阻塞线程1。在此之后的整个过程中IsRelease的值都是true.如果

想将IsRelease的值回复成false,就必须再调用_manualResetEvent.Reset()的方法。

  

       如果是_autoResetEvent.set(),那么_autoResetEvent.WaitOne()后会自动将IsRelease的值自动设置为false.

这就是为什么一个叫auto,一个叫manual.

_______________________________________________________________________________

  AutoResetEvent 允许线程通过发信号互相通信。通常,此通信涉及线程需要独占访问的资源。

  线程通过调用 AutoResetEvent 上的 WaitOne 来等待信号。如果 AutoResetEvent 处于非终止状态,则该线程阻塞,并等待当前控制资源的线程通过调用 Set 发出资源可用的信号。

  调用 SetAutoResetEvent 发信号以释放等待线程。AutoResetEvent 将保持终止状态,直到一个正在等待的线程被释放,然后自动返回非终止状态。如果没有任何线程在等待,则状态将无限期地保持为终止状态。

  可以通过将一个布尔值传递给构造函数来控制 AutoResetEvent 的初始状态,如果初始状态为终止状态,则为 true;否则为 false

  通俗的来讲只有等myResetEven.Set()成功运行后,myResetEven.WaitOne()才能够获得运行机会;Set是发信号,WaitOne是等待信号,只有发了信号,等待的才会执行。如果不发的话,WaitOne后面的程序就永远不会执行。下面我们来举一个例子:我去书店买书,当我选中一本书后我会去收费处付钱,付好钱后再去仓库取书。这个顺序不能颠倒,我作为主线程,收费处和仓库做两个辅助线程,代码如下:

using System;
using System.Linq;
using System.Activities;
using System.Activities.Statements;
using System.Threading;

namespace CaryAREDemo
{
class Me
{
const int numIterations = 550;
static AutoResetEvent myResetEvent = new AutoResetEvent(false);
static AutoResetEvent ChangeEvent = new AutoResetEvent(false);
//static ManualResetEvent myResetEvent = new ManualResetEvent(false);
//static ManualResetEvent ChangeEvent = new ManualResetEvent(false);
static int number; //这是关键资源

static void Main()
{
Thread payMoneyThread = new Thread(new ThreadStart(PayMoneyProc));
payMoneyThread.Name = "付钱线程";
Thread getBookThread = new Thread(new ThreadStart(GetBookProc));
getBookThread.Name = "取书线程";
payMoneyThread.Start();
getBookThread.Start();

for (int i = 1; i <= numIterations; i++)
{
Console.WriteLine("买书线程:数量{0}", i);
number = i;
//Signal that a value has been written.
myResetEvent.Set();
ChangeEvent.Set();
Thread.Sleep(0);
}
payMoneyThread.Abort();
getBookThread.Abort();
}

static void PayMoneyProc()
{
while (true)
{
myResetEvent.WaitOne();
//myResetEvent.Reset();
Console.WriteLine("{0}:数量{1}", Thread.CurrentThread.Name, number);
}
}
static void GetBookProc()
{
while (true)
{
ChangeEvent.WaitOne();
// ChangeEvent.Reset();
Console.WriteLine("{0}:数量{1}", Thread.CurrentThread.Name, number);
Console.WriteLine("------------------------------------------");
Thread.Sleep(0);
}
}
}
}

运行结果如下:

  

  AutoResetEvent与ManualResetEvent的区别:

  他们的用法\声明都很类似,Set方法将信号置为发送状态 Reset方法将信号置为不发送状态WaitOne等待信号的发送。其实,从名字就可以看出一个手动,
一个自动,这个手动和自动实际指的是在Reset方法的处理上,如下面例子:

public AutoResetEvent autoevent = new AutoResetEvent(true);
public ManualResetEvent manualevent = new ManualResetEvent(true);

  默认信号都处于发送状态,

autoevent.WaitOne();
manualevent.WaitOne();

  如果 某个线程调用上面该方法,则当信号处于发送状态时,该线程会得到信号,得以继续执行。差别就在调用后,autoevent.WaitOne()每次只允许一个线程进入,当某个线程得到信号(也就是有其他线程调用了autoevent.Set()方法后)后,autoevent会自动又将信号置为不发送状态,则其他调用WaitOne的线程只有继续等待.也就是说,autoevent一次只唤醒一个线程。而manualevent则可以唤醒多个线程,因为当某个线程调用了set方法后,其他调用waitone的线程获得信号得以继续执行,而manualevent不会自动将信号置为不发送.也就是说,除非手工调用了manualevent.Reset().方法,则manualevent将一直保持有信号状态,manualevent也就可以同时唤醒多个线程继续执行。如果上面的程序换成ManualResetEvent的话,就需要在waitone后面做下reset。

 

AutoResetEvent不是线程的非终止状态是事件的非终止状态,
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()方法把它设置为无信号状态。这里用它的有信号状态来表示一个线程的结束。

?
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设置为有信号状态
  }
}
本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
C#多线程:深入了解线程同步lock,Monitor,Mutex,同步事件和等待句柄(中)...
.Net CLR 中的同步机制(三): AutoResetEvent和ManualResetEvent
C #中的几个线程同步对象方法
C#多线程
C# Mutex对象的使用
AutoResetEvent与ManualResetEvent区别
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服