首頁 後端開發 C#.Net教程 .NET 同步與非同步 之 EventWaitHandle

.NET 同步與非同步 之 EventWaitHandle

Apr 11, 2017 pm 02:05 PM


在前一篇我們已經提到Mutex和本篇的主角們直接或間接繼承自WaitHandle:

陣列

裡的所有成員。於控制等待逾時的重載方法,請自行參考。返回。

執行緒相關性
  • #Mutex與Monitor一樣,是具有執行緒相關性的。到過,只有透過Monitor.Enter()/TryEnter()獲得物件鎖定的執行緒才能呼叫Pulse()/Wait()/Exit();同樣的,只有獲得Mutex擁有權的執行緒才能執行ReleaseMutex()方法,否則就會引發異常。這就是所謂的線程相關性。

  • 相反,EventWaitHandle以及它的衍生類別AutoResetEvent和ManualResetEvent都是線程無關的。任何執行緒都可以發送訊號給EventWaitHandle,喚醒阻塞在上面的執行緒。
  • 下一篇要提到的Semaphore也是線程無關的。

  •  
  • Event通知

EventWaitHandle、AutoResetEvent、ManualResetEvent名字裡都有一個“Event”,不過這跟. net的本身的

事件

機製完全沒有關係,它不涉及任何委託或事件處理程式。相對於我們之前碰到的Monitor和Mutex需要執行緒去爭奪「鎖」而言,我們可以把它們理解為一些需要執行緒等待的「事件」。線程透過等待這些事件的“發生”,把自己阻塞起來。一旦「事件」完成,被阻塞的執行緒在收到訊號後就可以繼續工作。

      為了配合WaitHandle上的3個靜態方法SingnalAndWait()/WailAny()/WaitAll(),EventWaitHandle提供了自己獨有的,使「Event」完成和重新開始的方法:
  • bool:Set():英文版MSDN:Sets the state of the event to signaled, allowing one or more waiting threads to proceed;中文版MSDN:將事件
  • 狀態
  • 設定為終止狀態,允許一個或多個等待執行緒繼續。初看「signaled」和「終止」似乎不對應,細想起來這兩者的說法其實也不矛盾。事件如果在進行中,當然就沒有“終止”,那麼

    其它

    線程就需要等待;一旦事件完成,那麼事件就“終止”了,於是我們發送信號喚醒等待的線程,所以“信號已發送”狀態也是合理的。兩個小細節:
  1. 無論中文或英文版,都提到這個方法都是可以讓「一個」或「多個」等待執行緒「繼續/Proceed」(注意不是「喚醒」)。所以這個方法在「喚醒」這個動作上是類似Monitor.Pulse()和Monitor.PulseAll()的。至於什麼時候類似Pulse(),又在什麼時候類似PulseAll(),往下看。

  2. 這個方法有bool型的回傳值:如果該操作成功,則為true;否則,為false。不過MSDN並沒有告訴我們,什麼時候執行會失敗,你只有找微軟MVP問了。

  • bool:Reset():Sets the state of the event to nonsignaled, causing threads to block. 將事件狀態設為非終止狀態,導致執行緒封鎖。 同樣,我們需要明白「nonsignaled」和「非終止」是一回事。同樣的是,仍然有個無厘頭的回傳值。 Reset()的作用,相當於讓事件重新開始處於“進行中”,那麼此後所有WaitOne()/WaitAll()/WaitAny()/SignalAndWait()這個事件的線程都會再次被擋在門外。

  •  

    建構子

    來看看EventWaitHandle眾多建構子中最簡單的一個:

    • EventWaitHandle(Boolean initialState, EventResetMode mode):初始化EventWaitHandle類別的新實例,並指定等待句柄最初是否處於終止狀態,以及它是自動重置還是手動重置。大多數時候我們會在第一個參數裡使用false,這樣新實例會缺省為「非終止」狀態。第二個參數EventResetMode是個列舉,總共兩個值:

    1. EventResetMode.AutoReset:當Set()被呼叫目前EventWaitHandle轉入終止狀態時,若有執行緒阻塞在目前EventWaitHandle上,那麼在釋放一個執行緒後EventWaitHandle就會自動重置(相當於自動呼叫Reset())再次轉入非終止狀態,剩餘的原來阻塞的線程(如果有的話)還會繼續阻塞。如果呼叫Set()後本沒有執行緒阻塞,那麼EventWaitHandle將保持「終止」狀態直到一個執行緒嘗試等待該事件,而這個執行緒不會被阻塞,此後EventWaitHandle才會自動重置並阻塞那之後的所有執行緒。

    2. EventResetMode.ManualReset:當終止時,EventWaitHandle 釋放所有等待的線程,並在手動重置前,即Reset()被呼叫前,一直保持終止狀態。

      好了,現在我們可以清楚的知道Set()在什麼時候分別類似於Monitor.Pulse()/PulseAll()了:

    • #當EventWaitHandle工作在AutoReset模式下,就喚醒功能而言,Set()與Monitor.Pulse()類似。此時,Set()只能喚醒眾多(如果有多個的話)被阻塞執行緒中的一個。但兩者仍有些差別:

    1. Set()的作用不僅僅是“喚醒”而是“釋放”,可以讓執行緒繼續工作(proceed);相反, Pulse()喚醒的執行緒只是重新進入Running狀態,參與物件鎖定的爭奪,誰都不能保證它一定會獲得物件鎖定。

    2. Pulse()的已被呼叫的狀態不會被維護。因此,如果在沒有等待執行緒時呼叫Pulse(),那麼下一個呼叫Monitor.Wait()的執行緒仍然會被阻塞,就像Pulse() 沒有被呼叫過。也就是說Monitor.Pulse()只在呼叫當時發揮作用,並不像Set()的作用會持續到下一個WaitXXX()。

  • 在一個工作在ManualReset模式下的EventWaitHandle的Set()方法被呼叫時,它所扮演的喚醒作用與Monitor.PulseAll()類似,所有被阻塞的執行緒都會收到訊號被喚醒。而兩者的差異與上面完全相同。

  •   來看看EventWaitHandle的其它建構子:

    • EventWaitHandle(Boolean initialState, EventResetMode mode, String name):前兩個參數我們已經看過,第三個參數name用於在系統範圍內指定同步事件的名稱。是的,正如我們在Mutex一篇中提到的,由於父類WaitHandle是具有跨進程域的能力的,因此跟Mutex一樣,我們可以創建一個全域的EventWaitHandle,讓後將它用於進程間的通知。注意,name仍然是大小寫敏感的,仍然有命名前綴的問題跟,你可以參考這裡。當name為null或空字串時,這等效於建立一個局部的未命名的EventWaitHandle。仍然同樣的還有,可能會因為已經系統中已經有同名的EventWaitHandle而只回傳一個實例表示同名的EventWaitHandle。所以最後仍舊同樣地,如果你需要知道這個EventWaitHandle是否由你最早創建,你需要使用以下兩個建構函式之一。

    • EventWaitHandle(Boolean initialState, EventResetMode mode, String name, out Boolean createdNew):createdNew用來表示是否成功建立了EventWaitHandle,true表示成功,false表示已經存在同名的事件。

    • EventWaitHandle(Boolean initialState, EventResetMode mode, String name, out Boolean createdNew, EventWaitHandleSecurity):關於安全的問題,直接查看這個建構子上的範例吧。全域MutexEventWaitHandle的安全性問題應該相對Mutex更需要注意,因為有可能駭客程式用相同的事件名稱對你的執行緒發送訊號或進行組織,那樣可能會嚴重危害你的業務邏輯。

     

    MSDN Demo


    #
    using System;using System.Threading;public class Example
    {    // The EventWaitHandle used to demonstrate the difference    // between AutoReset and ManualReset synchronization events.    //    private static EventWaitHandle ewh;    // A counter to make sure all threads are started and    // blocked before any are released. A Long is used to show    // the use of the 64-bit Interlocked methods.    //    private static long threadCount = 0;    // An AutoReset event that allows the main thread to block    // until an exiting thread has decremented the count.    //    private static EventWaitHandle clearCount = 
            new EventWaitHandle(false, EventResetMode.AutoReset);
    
        [MTAThread]    public static void Main()
        {        // Create an AutoReset EventWaitHandle.        //        ewh = new EventWaitHandle(false, EventResetMode.AutoReset);        // Create and start five numbered threads. Use the        // ParameterizedThreadStart delegate, so the thread        // number can be passed as an argument to the Start 
            // method.
            for (int i = 0; i <= 4; i++)
            {
                Thread t = new Thread(                new ParameterizedThreadStart(ThreadProc)
                );
                t.Start(i);
            }        // Wait until all the threads have started and blocked.        // When multiple threads use a 64-bit value on a 32-bit        // system, you must access the value through the        // Interlocked class to guarantee thread safety.        //        while (Interlocked.Read(ref threadCount) < 5)
            {
                Thread.Sleep(500);
            }        // Release one thread each time the user presses ENTER,        // until all threads have been released.        //        while (Interlocked.Read(ref threadCount) > 0)
            {
                Console.WriteLine("Press ENTER to release a waiting thread.");
                Console.ReadLine();            // SignalAndWait signals the EventWaitHandle, which            // releases exactly one thread before resetting, 
                // because it was created with AutoReset mode. 
                // SignalAndWait then blocks on clearCount, to 
                // allow the signaled thread to decrement the count            // before looping again.            //            WaitHandle.SignalAndWait(ewh, clearCount);
            }
            Console.WriteLine();        // Create a ManualReset EventWaitHandle.        //        ewh = new EventWaitHandle(false, EventResetMode.ManualReset);        // Create and start five more numbered threads.        //        for(int i=0; i<=4; i++)
            {
                Thread t = new Thread(                new ParameterizedThreadStart(ThreadProc)
                );
                t.Start(i);
            }        // Wait until all the threads have started and blocked.        //        while (Interlocked.Read(ref threadCount) < 5)
            {
                Thread.Sleep(500);
            }        // Because the EventWaitHandle was created with        // ManualReset mode, signaling it releases all the        // waiting threads.        //        Console.WriteLine("Press ENTER to release the waiting threads.");
            Console.ReadLine();
            ewh.Set();
    
        }    public static void ThreadProc(object data)
        {        int index = (int) data;
    
            Console.WriteLine("Thread {0} blocks.", data);        // Increment the count of blocked threads.
            Interlocked.Increment(ref threadCount);        // Wait on the EventWaitHandle.        ewh.WaitOne();
    
            Console.WriteLine("Thread {0} exits.", data);        // Decrement the count of blocked threads.
            Interlocked.Decrement(ref threadCount);        // After signaling ewh, the main thread blocks on        // clearCount until the signaled thread has 
            // decremented the count. Signal it now.        //        clearCount.Set();
        }
    }
    登入後複製


     

    以上是.NET 同步與非同步 之 EventWaitHandle的詳細內容。更多資訊請關注PHP中文網其他相關文章!

    本網站聲明
    本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

    熱AI工具

    Undresser.AI Undress

    Undresser.AI Undress

    人工智慧驅動的應用程序,用於創建逼真的裸體照片

    AI Clothes Remover

    AI Clothes Remover

    用於從照片中去除衣服的線上人工智慧工具。

    Undress AI Tool

    Undress AI Tool

    免費脫衣圖片

    Clothoff.io

    Clothoff.io

    AI脫衣器

    Video Face Swap

    Video Face Swap

    使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

    熱工具

    記事本++7.3.1

    記事本++7.3.1

    好用且免費的程式碼編輯器

    SublimeText3漢化版

    SublimeText3漢化版

    中文版,非常好用

    禪工作室 13.0.1

    禪工作室 13.0.1

    強大的PHP整合開發環境

    Dreamweaver CS6

    Dreamweaver CS6

    視覺化網頁開發工具

    SublimeText3 Mac版

    SublimeText3 Mac版

    神級程式碼編輯軟體(SublimeText3)

    C語言各種符號的使用方法 C語言各種符號的使用方法 Apr 03, 2025 pm 04:48 PM

    C 語言中符號的使用方法涵蓋算術、賦值、條件、邏輯、位運算符等。算術運算符用於基本數學運算,賦值運算符用於賦值和加減乘除賦值,條件運算符用於根據條件執行不同操作,邏輯運算符用於邏輯操作,位運算符用於位級操作,特殊常量用於表示空指針、文件結束標記和非數字值。

    char在C語言字符串中的作用是什麼 char在C語言字符串中的作用是什麼 Apr 03, 2025 pm 03:15 PM

    在 C 語言中,char 類型在字符串中用於:1. 存儲單個字符;2. 使用數組表示字符串並以 null 終止符結束;3. 通過字符串操作函數進行操作;4. 從鍵盤讀取或輸出字符串。

    char在C語言中如何處理特殊字符 char在C語言中如何處理特殊字符 Apr 03, 2025 pm 03:18 PM

    C語言中通過轉義序列處理特殊字符,如:\n表示換行符。 \t表示製表符。使用轉義序列或字符常量表示特殊字符,如char c = '\n'。注意,反斜杠需要轉義兩次。不同平台和編譯器可能有不同的轉義序列,請查閱文檔。

    c#多線程和異步的區別 c#多線程和異步的區別 Apr 03, 2025 pm 02:57 PM

    多線程和異步的區別在於,多線程同時執行多個線程,而異步在不阻塞當前線程的情況下執行操作。多線程用於計算密集型任務,而異步用於用戶交互操作。多線程的優勢是提高計算性能,異步的優勢是不阻塞 UI 線程。選擇多線程還是異步取決於任務性質:計算密集型任務使用多線程,與外部資源交互且需要保持 UI 響應的任務使用異步。

    char與wchar_t在C語言中的區別 char與wchar_t在C語言中的區別 Apr 03, 2025 pm 03:09 PM

    在 C 語言中,char 和 wchar_t 的主要區別在於字符編碼:char 使用 ASCII 或擴展 ASCII,wchar_t 使用 Unicode;char 佔用 1-2 個字節,wchar_t 佔用 2-4 個字節;char 適用於英語文本,wchar_t 適用於多語言文本;char 廣泛支持,wchar_t 依賴於編譯器和操作系統是否支持 Unicode;char 的字符範圍受限,wchar_t 的字符範圍更大,並使用專門的函數進行算術運算。

    char在C語言中如何進行類型轉換 char在C語言中如何進行類型轉換 Apr 03, 2025 pm 03:21 PM

    在 C 語言中,char 類型轉換可以通過:強制類型轉換:使用強制類型轉換符將一種類型的數據直接轉換為另一種類型。自動類型轉換:當一種類型的數據可以容納另一種類型的值時,編譯器自動進行轉換。

    char和unsigned char的區別是什麼 char和unsigned char的區別是什麼 Apr 03, 2025 pm 03:36 PM

    char 和 unsigned char 是存儲字符數據的兩種數據類型,主要區別在於處理負數和正數的方式:值範圍:char 有符號 (-128 到 127),unsigned char 無符號 (0 到 255)。負數處理:char 可以存儲負數,unsigned char 不能。位模式:char 最高位表示符號,unsigned char 無符號位。算術運算:char 和 unsigned char 作為有符號和無符號類型,其算術運算方式不同。兼容性:char 和 unsigned char

    char數組在C語言中如何使用 char數組在C語言中如何使用 Apr 03, 2025 pm 03:24 PM

    char 數組在 C 語言中存儲字符序列,聲明為 char array_name[size]。訪問元素通過下標運算符,元素以空終止符 '\0' 結尾,用於表示字符串終點。 C 語言提供多種字符串操作函數,如 strlen()、strcpy()、strcat() 和 strcmp()。

    See all articles