.Net下的线程同步:Part 1 of N--(EventWaitHandle:AutoResetEvent and ManualResetEvent)

        写本系列的想法源自: 改善 C#程序的建议6:在线程同步中使用信号量,但是其中只是提到了AutoResetEvent、ManualResetEvent的用法,我觉得不够全面,因此想对.net下的同步方法做个逐一介绍。

        本文主要参考:Beginner's Guide to Threading in .NET: Part 3 of n,可以说是对于原文修改再加上个人理解和拆分后的译作。原文作者Sacha Barber,为文甚为严谨周详,相当佩服。

         闲话少说,本系列有以下几篇:

  • 1)EventWaitHandle
  • 2)Semaphores
  • 3)Mutex
  • 4)关键区域(Critical Sections)
  • 5)其它同步对象

        本文为1)EventWaitHandle。

        让我们先来看看,WaitHandle与EventWaitHandle,Semaphores 和 Mutex的层次关系:

       

        .Net中WaitHandle采用信号与等待机制。当一个线程在WaitHandle上等待时,该线程被阻塞,直到这个WaitHandle接收到信号,此时等待线程解除阻塞,继续运行。WaitHandle的工作机制可与铁路交叉口的情况进行类比,如下图所示:



        Net中与WaitHandle相关的类层次如下:

  • System.Threading.WaitHandle
    • System.Threading.EventWaitHandle
    • System.Threading.Mutex
    • System.Threading.Semaphore

        从上面的层次关系中可以看出, System.Threading.WaitHandle 是其它几个我们常用同步类的基类. 在介绍其子类前,我们先介绍几个重要的常用函数。

       SignalAndWait:调用该函数可以向某个线程的WaitHandle 发送信号,而其它线程的WaitHandle  则继续等待信号。
       WaitAll (Static method on WaitHandle):可以将一组WaitHandle作为参数 传给该函数,而这组WaitHandle都将等待信号。
       WaitAny (Static method on WaitHandle):可以将一组WaitHandle作为参数 传给该函数,不同的是,只要该组中的任何一个WaitHandle接收到信号,则函数将退出。
       WaitOne:调用该函数的WaitHandle将被阻塞,直到接收到信号。

        下面我们将专注于 System.Threading.EventWaitHandle.对于EventWaitHandle ,大多数情况下,我们主要使用其两个子类:ManualResetEvent和AutoResetEvent

AutoResetEvent

From MSDN:

"A thread waits for a signal by calling WaitOne on the AutoResetEvent. If theAutoResetEvent is in the non-signaled state, the thread blocks, waiting for the thread that currently controls the resource to signal that the resource is available by callingSet.

Calling Set signals AutoResetEvent to release a waiting thread.AutoResetEvent remains signaled until a single waiting thread is released, and then automatically returns to the non-signaled state. If no threads are waiting, the state remains signaled indefinitely."

         当我们使用AutoResetEvent时,如果调用了Set,则其处于signaled状态,那么第一个解除阻塞的线程会自动将AutoResetEvent 置为non-signaled状态,那么其它等待AutoResetEvent 的线程要一直等待,直到AutoResetEvent 下一次接收到信号。示例代码:

using System;
using System.Threading;

namespace AutoResetEventTest
{
    class Program
    {
        public static Thread T1;
        public static Thread T2;
        //This AutoResetEvent starts out non-signalled
        public static AutoResetEvent ar1 = new AutoResetEvent(false);
        //This AutoResetEvent starts out signalled
        public static AutoResetEvent ar2 = new AutoResetEvent(true);

        static void Main(string[] args)
        {

            T1 = new Thread((ThreadStart)delegate
                {
                    Console.WriteLine(
                        "T1 is simulating some work by sleeping for 5 secs");
                    //calling sleep to simulate some work
                    Thread.Sleep(5000);
                    Console.WriteLine(
                        "T1 is just about to set AutoResetEvent ar1");
                    //alert waiting thread(s)
                    ar1.Set();
                });

            T2 = new Thread((ThreadStart)delegate
            {
                //wait for AutoResetEvent ar1, this will wait for ar1 to be signalled
                //from some other thread
                Console.WriteLine(
                    "T2 starting to wait for AutoResetEvent ar1, at time {0}", 
                    DateTime.Now.ToLongTimeString());
                ar1.WaitOne();
                Console.WriteLine(
                    "T2 finished waiting for AutoResetEvent ar1, at time {0}", 
                    DateTime.Now.ToLongTimeString());

                //wait for AutoResetEvent ar2, this will skip straight through
                //as AutoResetEvent ar2 started out in the signalled state
                Console.WriteLine(
                    "T2 starting to wait for AutoResetEvent ar2, at time {0}",
                    DateTime.Now.ToLongTimeString());
                ar2.WaitOne();
                Console.WriteLine(
                    "T2 finished waiting for AutoResetEvent ar2, at time {0}",
                    DateTime.Now.ToLongTimeString());
            });
            
            T1.Name = "T1";
            T2.Name = "T2";
            T1.Start();
            T2.Start();
            Console.ReadLine();
        }
    }
}
结果如下图:

ManualResetEvent

From MSDN:

"When a thread begins an activity that must complete before other threads proceed, it callsReset to putManualResetEvent in the non-signaled state. This thread can be thought of as controlling theManualResetEvent. Threads that callWaitOne on the ManualResetEvent will block, awaiting the signal. When the controlling thread completes the activity, it calls Set to signal that the waiting threads can proceed. All waiting threads are released.

Once it has been signaled, ManualResetEvent remains signaled until it is manually reset. That is, calls toWaitOne return immediately."

        在使用ManualResetEvent时,如果ManualResetEvent被置为signaled状态,那么在其上等待的所有线程都被解除阻塞直到ManualResetEvent又被重置,即处于non-signaled状态。示例代码如下:

using System;
using System.Threading;

namespace ManualResetEventTest
{
    /// <summary>
    /// This simple class demonstrates the usage of an ManualResetEvent
    /// in 2 different scenarios, bith in the non-signalled state and the 
    /// signalled state
    /// </summary>
    class Program
    {
        public static Thread T1;
        public static Thread T2;
        public static Thread T3;
        //This ManualResetEvent starts out non-signalled
        public static ManualResetEvent mr1 = new ManualResetEvent(false);

        static void Main(string[] args)
        {
            T1 = new Thread((ThreadStart)delegate
            {
                Console.WriteLine(
                    "T1 is simulating some work by sleeping for 5 secs");
                //calling sleep to simulate some work
                Thread.Sleep(5000);
                Console.WriteLine(
                    "T1 is just about to set ManualResetEvent ar1");
                //alert waiting thread(s)
                mr1.Set();
            });

            T2 = new Thread((ThreadStart)delegate
            {
                //wait for ManualResetEvent mr1, this will wait for ar1
                //to be signalled from some other thread
                Console.WriteLine(
                    "T2 starting to wait for ManualResetEvent mr1, at time {0}",
                    DateTime.Now.ToLongTimeString());
                mr1.WaitOne();
                Console.WriteLine(
                    "T2 finished waiting for ManualResetEvent mr1, at time {0}",
                    DateTime.Now.ToLongTimeString());
            });

            T3 = new Thread((ThreadStart)delegate
            {
                //wait for ManualResetEvent mr1, this will wait for ar1
                //to be signalled from some other thread
                Console.WriteLine(
                    "T3 starting to wait for ManualResetEvent mr1, at time {0}",
                    DateTime.Now.ToLongTimeString());
                mr1.WaitOne();
                Console.WriteLine(
                    "T3 finished waiting for ManualResetEvent mr1, at time {0}",
                    DateTime.Now.ToLongTimeString());
            });

            T1.Name = "T1";
            T2.Name = "T2";
            T3.Name = "T3";
            T1.Start();
            T2.Start();
            T3.Start();
            Console.ReadLine();

        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值