C#線程學習筆記五:線程同步--事件構造

    本筆記摘抄自:https://www.cnblogs.com/zhili/archive/2012/07/23/Event_Constructor.html,記錄一下學習過程以備後續查用。html

    前面講的線程同步主要是用戶模式的(CLR Via C# 一書中是這麼定義的,書中說到線程同步分兩種:1、用戶模式構造 2、內核模式構造),對於內核模式構造函數

(指的的是構造操做系內核對象),咱們使用.NET Framework中的類如AutoResetEvent、Semaphore中方法來實現線程同步,其實其內部是調用操做系統中的內核學習

對象來實現的線程同步,此時就會將線程從託管代碼轉爲內核代碼。而用戶模式構造,由於沒有調用操做系統內核對象,因此線程只會在用戶的託管代碼上執行。ui

    1、WaitHandle基類介紹spa

    System.Threading命名空間中提供了一個WaitHandle 的抽象基類,此類就是包裝了一個Windows內核對象的句柄(句柄能夠理解爲標示了對象實例的一個數字,操作系統

具體你們能夠查資料深刻理解下,在這裏只是提出理解句柄也是很重要的)。線程

    在.NET Framework中提供了從WaitHandle類派生的類,它們的繼承關係爲:3d

    WaitHandlerest

     EventWaitHandlecode

            AutoResetEvent

            ManualResetEvent

        Semaphore

        Mutex

    當咱們用構造函數來實例化AutoResetEvent、ManualResetEvent、Semaphore、Mutex這些類的對象時,其內部都調用了Win32 CreateEvent或者

CreateEvent函數或者CreateSemaphore或者CreateMutex函數,這些函數調用返回的句柄值都保存在WaitHandle基類定義的SafeWaitHandle字段中。

    2、事件(Event)類實現線程同步

    2.1 AutoResetEvent (自動重置事件)

    2.1.1先講講AutoResetEvent類的構造函數,其定義爲:

    public AutoResetEvent(bool initialState);

    構造函數中用一個bool 類型的初始狀態來設置AutoResetEvent對象的狀態。若是要將AutoResetEvent對象的初始狀態設置爲終止,則傳入bool值爲true;

若要設置爲非終止,則傳入bool值爲false。 

    2.2.2WaitOne方法定義:

    public virtual bool WaitOne(int millisecondsTimeout);

    該方法用來阻塞線程,當在指定的時間間隔尚未收到一個信號時,將返回false。

    調用Set方法發信號來釋放等待線程。

    在使用過程當中WaitOne方法和Set方法都是成對出現的:

        一個用於阻塞線程,等待信號;

        一個用來釋放等待線程(就是說調用set方法來發送一個信號,此時WaitOne接受到信號,就釋放阻塞的線程,線程就能夠繼續運行。)

    線程經過調用AutoResetEvent的WaitOne方法來等待信號,若是AutoResetEvent對象爲非終止狀態,則線程被阻止,直到線程調用Set方法來恢復線程執行;

若是AutoResetEvent爲終止狀態時,則線程不會被阻止,此時AutoResetEvent將當即釋放線程並返回爲非終止狀態(指出有線程在使用資源的一種狀態)。

    下面代碼演示AutoResetEvent的使用:

    class Program
    {
        //建立對象
        public static AutoResetEvent autoResetEvent = new AutoResetEvent(false);

        static void Main(string[] args)
        {
            #region 線程同步:AutoResetEvent的使用
            Console.WriteLine("Main thread start run at: " + DateTime.Now.ToLongTimeString());
            Thread thread = new Thread(WaitOneMethod);
            thread.Start();

            //阻塞主線程3秒鐘
            Thread.Sleep(3000);

            //釋放線程
            autoResetEvent.Set();
            Console.Read();
            #endregion
        }

        /// <summary>
        /// WaitOne方法
        /// </summary>
        public static void WaitOneMethod()
        {
            autoResetEvent.WaitOne();
            Console.WriteLine("Method restart run at: " + DateTime.Now.ToLongTimeString());
        }
    }

    運行結果以下:

    若建立對象時,把它改成public static AutoResetEvent autoResetEvent = new AutoResetEvent(true); ,看到的輸出結果的時間就是同樣的了。由於設置爲True時,

表示此時已經爲終止狀態了。所以,autoResetEvent.Set()能夠理解爲將autoResetEvent的狀態設置爲終止狀態,於是釋放線程。 

    上面用到的是沒帶參數的WaitOne方法,該方法表示無限制阻塞線程,直到收到一個事件爲止(經過Set方法來發送一個信號)。

    經過bool WaitOne(int millisecondsTimeout),當超時時,線程即便沒收到Set發來的信號,也將再也不阻塞線程而讓它繼續運行,只是WaitOne方法返回的值不同:

    當收到Set信號時返回值爲True,不然返回值爲false。

    下面代碼演示WaitOne(millisecondsTimeout)的使用:

    class Program
    {
        //建立對象
        public static AutoResetEvent autoResetEvent = new AutoResetEvent(false);

        static void Main(string[] args)
        {
            #region 線程同步:WaitOne(millisecondsTimeout)的使用
            Console.WriteLine("Main thread start run at: " + DateTime.Now.ToLongTimeString());
            Thread thread = new Thread(WaitOneTimeoutMethod);
            thread.Start();

            //阻塞主線程3秒鐘
            Thread.Sleep(3000);

            //釋放線程
            autoResetEvent.Set();
            Console.Read();
            #endregion
        }

        /// <summary>
        /// WaitOneTimeout方法
        /// </summary>
        public static void WaitOneTimeoutMethod()
        {
            if (autoResetEvent.WaitOne(2000))
            {
                Console.WriteLine("Get signal to work.");
                Console.WriteLine("Method restart run at: " + DateTime.Now.ToLongTimeString());
            }
            else
            {
                Console.WriteLine("Time out to work.");
                Console.WriteLine("Method restart run at: " + DateTime.Now.ToLongTimeString());
            }
        }
    }

    運行結果以下:

    若把Thread.Sleep(3000);設爲Thread.Sleep(1000);,此時線程將收到Set發過來的信號,獲得的結果將是Get signal to work,時間相差就只有1秒了。

    2.2 ManualResetEvent(手動重置事件)

    ManualResetEvent和AutoResetEvent的使用方法很相似,由於他們都是從EventWaitHandle類派生的,不過他們仍是有些區別:

    2.2.1AutoResetEvent 爲終止狀態(true)時,若是線程調用WaitOne方法的話線程是不會被阻止的。此時AutoResetEvent將當即釋放線程並返回到非終止狀態(false),

在這以後若是線程再次調用WaitOne方法的話,線程將被阻止。(注:調用WaitOne方法自動改變狀態,僅對初始狀態爲終止狀態時有效。)

    2.2.2ManualResetEvent爲終止狀態(true)時,若是線程調用WaitOne方法的話線程也是不會被阻止的。此ManualResetEvent將當即釋放線程但不會返回到非終止

狀態(false),除非咱們手動將狀態改成終止狀態(false),不然在這以後若是線程再次調用WaitOne方法的話,線程不會被阻止。

    下面代碼演示二者的區別:

    class Program
    {
        //建立對象
        public static AutoResetEvent autoResetEvent = new AutoResetEvent(false);
        public static ManualResetEvent manualResetEvent = new ManualResetEvent(false);

        static void Main(string[] args)
        {
            #region 線程同步:ManualResetEvent的使用
            Console.WriteLine("Main thread start run at: " + DateTime.Now.ToLongTimeString());

            Thread threadAuto = new Thread(AutoResetEventMethod);
            threadAuto.Start();
            autoResetEvent.Set();
            threadAuto = new Thread(AutoResetEventMethod);
            threadAuto.Start();

            Thread threadManual = new Thread(ManualResetEventMethod);
            threadManual.Start();
            manualResetEvent.Set();
            threadManual = new Thread(ManualResetEventMethod);
            threadManual.Start();

            Console.Read();
            #endregion
        }

        /// <summary>
        /// AutoResetEvent方法
        /// </summary>
        public static void AutoResetEventMethod()
        {
            autoResetEvent.WaitOne();
            Console.WriteLine("Autoresetevent method restart run at: " + DateTime.Now.ToLongTimeString());
        }

        /// <summary>
        /// ManualResetEvent方法
        /// </summary>
        public static void ManualResetEventMethod()
        {
            manualResetEvent.WaitOne();
            Console.WriteLine("Manualresetevent method restart run at: " + DateTime.Now.ToLongTimeString());
        }
    }

    運行結果以下:

    2.3 跨進程之間同步

    內核模式構造可實現同一臺機器上的不一樣進程中的線程進行同步,所以可使用AutoResetEvent來實現此功能。此時須要對AutoResetEvent進行命名,

可是AutoResetEvent只提供了帶一個參數的構造函數,該如何實現呢?

    辦法仍是有的,由於AutoResetEvent是繼承自EventWaitHandle類,而EventWaitHandle類有多個構造函數。

    除了以前的方法建立AutoResetEvent對象外,還能夠經過EventWaitHandle autoEvent = new EventWaitHandle (false, EventResetMode.Auto,"My");這樣

的方式來構造AutoResetEvent對象,此方式指定了名稱。

    下面代碼演示跨進程之間的線程同步:

    第一個進程代碼:

    class Program
    {
        //建立對象
        public static EventWaitHandle autoEventFirst = new EventWaitHandle(false, EventResetMode.AutoReset, "First");
        public static EventWaitHandle autoEventSecond = new EventWaitHandle(false, EventResetMode.AutoReset, "Second");

        static void Main(string[] args)
        {
            #region 線程同步:跨進程之間的線程同步
            Console.WriteLine("First main thread start run at: " + DateTime.Now.ToLongTimeString());
            Thread thread = new Thread(EventWaitHandleMethod);
            thread.Start();

            //爲了有時間去啓動另一個進程
            Thread.Sleep(15000);
            autoEventFirst.Set();
            autoEventSecond.Set();
            Console.Read();
            #endregion
        }

        /// <summary>
        /// EventWaitHandle方法
        /// </summary>
        public static void EventWaitHandleMethod()
        {
            autoEventFirst.WaitOne();
            Console.WriteLine("First method start at:" + DateTime.Now.ToLongTimeString());
        }
    }

    第二個進程代碼:

    class Program
    {
        //建立對象
        public static EventWaitHandle autoEventSecond = new EventWaitHandle(false, EventResetMode.AutoReset, "Second");
        static void Main(string[] args)
        {
            Console.WriteLine("Second main thread start run at: " + DateTime.Now.ToLongTimeString());
            Thread thread = new Thread(EventWaitHandleMethod);
            thread.Start();
            Console.Read();
        }

        /// <summary>
        /// EventWaitHandle方法
        /// </summary>
        public static void EventWaitHandleMethod()
        {
            autoEventSecond.WaitOne();
            Console.WriteLine("Second method start at:" + DateTime.Now.ToLongTimeString());
        }
    }

    運行結果以下:

    從結果能夠看出,第一個進程的autoEventSecond.Set();信號發出後,第二個進程能夠收到並釋放線程。 

相關文章
相關標籤/搜索