2021-04-16

C#学习总结-AutoResetEvent和ManualResetEvent的区别

AutoResetEvent是如何工作的及其存在的缺陷

WaitOne方法

该方法阻塞当前线程并等待其他线程发送信号。如果收到信号,它将返回True,反之返回False。以下演示了如何调用该方法。

manualResetEvent.WaitOne();

在WaitOne方法的第二个重载版本中,我们可以指定当前线程等待信号的时间间隔。如果在时间间隔内,没有收到信号,方法将返回False并继续执行。以下代码演示了带时间间隔参数的WaitOne调用。

bool isSignalled = manualResetEvent.WaitOne(TimeSpan.FromSeconds(5));

我们指定了5秒作为WaitOne方法的参数,如果manualResetEvent对象在5秒内收到信号,它将isSignalled赋值为False。

Set方法

该方法用于给所有等待线程发送信号。 Set() 方法的调用使得ManualResetEvent对象的bool变量值为True,所有线程被释放并继续执行。下面是调用的例子:

manualResetEvent.Set();
Reset方法

一旦我们调用了ManualResetEvent对象的Set()方法,它的bool值就变为true,我们可以调用Reset()方法来重置该值,Reset()方法重置该值为False。以下是调用Reset方法的例子:

manualResetEvent.Reset();

如果我们想多次发送信号,那么我们必须在调用Set()方法后立即调用Reset()方法。下面我们来看AutoResetEvent的例子

 AutoResetEvent _autoResetEvent = new AutoResetEvent(false);
 private void BT_Temp_Click(object sender, RoutedEventArgs e)
   {
       Thread t1 = new Thread(this.Thread1Foo);
       t1.Start();
       Thread t2 = new Thread(this.Thread2Foo);
       t2.Start();
       Thread.Sleep(3000);
       _autoResetEvent.Set();
   }
   void Thread1Foo()
   {
       _autoResetEvent.WaitOne();
       MessageBox.Show("t1 end");
   }
   void Thread2Foo()
   {
       _autoResetEvent.WaitOne();
       MessageBox.Show("t2 end");
   }

该段代码运行的效果是,过3秒后,要么弹出“t1 end”,要么弹出“t2 end”,不会两个都弹出。也就是说,其中一个进行将会结束,而另一个进程永远不会结束,若需要两个线程都结束需要用ManualResetEvent。

该段代码运行的效果是,过3秒后,“t1 end”和“t2 end”,两个都被弹出。也就是说,两个进程都结束了。
这个特性就是说,AutoResetEvent只会给一个线程发送信号,而不会给多个线程发送信号。在我们需要同步多个线程的时候,就只能采用ManualResetEvent了。至于深层次的原因是,AutoResetEvent在set()之后,会将线程 状态自动置为false,而ManualResetEvent在Set()后,线程的状态就变为true了,必须手动ReSet()之后,才会重新将线程置为false。这也就是为什么他们的名字一个为Auto,一个为Manual的原因。为了更加充分的验证Manua lResetEvent的这点特性,我们再来看代码片段4

ManualResetEvent是如何工作的

ManualResetEvent 例子
ManualResetEvent _menuRestEvent = new ManualResetEvent(false);
private void BT_Temp_Click(object sender, RoutedEventArgs e)
{
    Thread t1 = new Thread(this.Thread1Foo);
    t1.Start();
    Thread t2 = new Thread(this.Thread2Foo);
    t2.Start();
    Thread.Sleep(3000);
    _menuRestEvent.Set();
}
void Thread1Foo()
{
    _menuRestEvent.WaitOne();
    MessageBox.Show("t1 end");
}
void Thread2Foo()
{
    _menuRestEvent.WaitOne();
    MessageBox.Show("t2 end");
}

这个特性就是说,AutoResetEvent只会给一个线程发送信号,而不会给多个线程发送信号。在我们需要同步多个线程的时候,就只能采用ManualResetEvent了。至于深层次的原因是,AutoResetEvent在set()之后,会将线程 状态自动置为false,而ManualResetEvent在Set()后,线程的状态就变为true了,必须手动ReSet()之后,才会重新将线程置为false。这也就是为什么他们的名字一个为Auto,一个为Manual的原因。为了更加充分的验证Manua lResetEvent的这点特性,我们再来看代码片段4

ManualResetEvent _menuRestEvent = new ManualResetEvent(false);
private void BT_Temp_Click(object sender, RoutedEventArgs e)
{
    Thread t1 = new Thread(this.Thread1Foo);
    t1.Start();
    Thread t2 = new Thread(this.Thread2Foo);
    t2.Start();
    Thread.Sleep(3000);
    _menuRestEvent.Set();
    //_menuRestEvent.Reset();           
}
void Thread1Foo()
{
    _menuRestEvent.WaitOne();
    MessageBox.Show("t1 step1 end");
    //睡1S,用于等待主线程_menuRestEvent.Reset();
    Thread.Sleep(1000);
    _menuRestEvent.WaitOne();
    MessageBox.Show("t1 step2 end");
}
void Thread2Foo()
{
    _menuRestEvent.WaitOne();
    MessageBox.Show("t2 step1 end");
    //睡1S,用于等待主线程_menuRestEvent.Reset();
    Thread.Sleep(1000);
    _menuRestEvent.WaitOne();
    MessageBox.Show("t2 step2 end");
}

如果我们将_menuRestEvent.Reset()进行注释,也就是说, _menuRestEvent.Set()后,线程的状态就是true状态的,程序运行的结果是"t1 step1 end"、“t1 step2 end”、“t1 step2 end”、"t2 step2 end"在3秒之后全部弹出。 而如果我们将//_menuRestEvent.Reset()的注释去掉,会发现"t1 step2 end"和"t2 step2 end"永远不会弹出。除非我们在主线程中再次对_menuRestEvent进行Set()。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值