2010-03-29 83 views

回答

51

每當你想控制你的應用程序中多個線程的執行。雖然這不僅意味着只有一個線程遞增計數器,但讓線程開始/停止或暫停事件。

WaitHandles - Auto/ManualResetEvent and Mutex

- 編輯 -

WaitHandle s爲的機制你 「使用」 控制線程的執行。它不是關於句柄在線程內不可訪問的;它關於在線程中使用它們。

這可能是一個很胖的例子,但請耐心等待;想想,一位女士給五個女孩五個不同的口哨,並告訴他們吹哨時,只要something會發生;這個過程是每個女孩吹口哨,女士會知道是誰吹哨子。

現在,它不是關於彼此分享哨子,它可能是爲了女士,用它們來「控制」女孩吹口哨的執行或過程。

因此,技術上的過程將是:

  1. 創建一個等待事件(ManualResetEvent的對象)
  2. 註冊事件,WaitHandle.WaitAny(events);
  3. 你做你的線程中執行操作後,.Set() ,它會告訴WaitHandle「我已經完成了!」。

例如,從所提供的鏈接中考慮示例。我已經添加了步驟來了解邏輯。這些不是硬編碼的步驟,但只是讓你可以理解。

class Test 
{ 
    static void Main() 
    { 
    //STEP 1: Create a wait handle 
     ManualResetEvent[] events = new ManualResetEvent[10];//Create a wait handle 
     for (int i=0; i < events.Length; i++) 
     { 
      events[i] = new ManualResetEvent(false); 
      Runner r = new Runner(events[i], i); 
      new Thread(new ThreadStart(r.Run)).Start(); 
     } 

    //STEP 2: Register for the events to wait for 
     int index = WaitHandle.WaitAny(events); //wait here for any event and print following line. 

     Console.WriteLine ("***** The winner is {0} *****", 
          index); 

     WaitHandle.WaitAll(events); //Wait for all of the threads to finish, that is, to call their cooresponding `.Set()` method. 

     Console.WriteLine ("All finished!"); 
    } 
} 


class Runner 
{ 
    static readonly object rngLock = new object(); 
    static Random rng = new Random(); 

    ManualResetEvent ev; 
    int id; 

    internal Runner (ManualResetEvent ev, int id) 
    { 
     this.ev = ev;//Wait handle associated to each object, thread in this case. 
     this.id = id; 
    } 

    internal void Run() 
    { 
    //STEP 3: Do some work 
     for (int i=0; i < 10; i++) 
     { 
      int sleepTime; 
      // Not sure about the thread safety of Random... 
      lock (rngLock) 
      { 
       sleepTime = rng.Next(2000); 
      } 
      Thread.Sleep(sleepTime); 
      Console.WriteLine ("Runner {0} at stage {1}", 
           id, i); 
     } 

    //STEP 4: Im done! 
     ev.Set(); 
    } 
} 
+0

WaitAll() - 用於等待集合中的所有句柄被釋放/發送信號......是否意味着Handles不會被其他線程訪問,直到它們沒有被釋放(其他線程應該等待在集合中的這些句柄)? – DotNetBeginner 2010-03-29 14:21:55

+1

@DotNetBeginner:'這是否意味着Handles不會被其他線程訪問;請參閱我的更新後的帖子,這是一個僅用於回答此問題的示例。 – 2010-03-30 06:16:15

+0

值得一提的是:'ev.Set()'最好放在'finally'塊 – 2016-10-15 08:39:14

6

的爲WaitAll和方法了WaitAny背後的想法是,當你有很多要並行運行的任務,他們是有用的。

例如,假設你有一個需要你在數組中的1000個項目需要在並行處理做一些處理工作。一個典型的Core 2 Duo +超線程只有4個邏輯處理器,因此,一次執行4個以上的線程並沒有多大意義(事實上,它的確如此,但這又是一個故事 - 我們會現在假裝並使用簡單的「每個處理器一個線程」模型)。所以4個線程,但1000個項目;你是做什麼?

一個選項是使用WaitAny方法。您啓動4個線程,並且每次WaitAny方法返回時都會啓動另一個線程,直到所有1000個項目都排隊。請注意,這是WaitAny的一個不好的例子,因爲你也可以將你的數組分成250個項目塊。不過,希望它能讓你瞭解WaitAny有用的情況。還有其他類似的情況,WaitAny可以很有意義。

但現在,讓我們回到場景中有4個線程,每個進程從1000項數組250項。使用此選項,可以使用WaitAll方法等待所有處理完成。

7

這是一個抽象類,你不直接使用它。具體派生類是ManualResetEvent,AutoResetEvent,Mutex和Semaphore。在您的工具箱中實現線程同步的重要類。它們繼承了WaitOne,WaitAll和WaitAny方法,用它們來檢測一個或多個線程是否表示等待狀態。

Manual/AutoResetEvent的典型使用場景是告訴線程退出或讓線程發出信號表明它已經發展到重要的序列點。信號量可以幫助您限制執行操作的線程數量。或者實現不應該與特定線程相關的線程同步。互斥體在那裏將一部分代碼的所有權分配給一個線程,鎖定語句通常也適用於那裏。

書已經寫了關於它。 Joe Duffy的Concurrent Programming in Windows是最新和最偉大的。如果你正在考慮編寫線程代碼,強烈推薦。

27

WaitHandle是兩個常用事件句柄的抽象基類:AutoResetEventManualResetEvent

這兩個類都允許一個線程「發信號」一個或多個其他線程。它們用於在線程之間進行同步(或序列化活動)。這是使用SetWaitOne(或WaitAll)方法完成的。例如:

主題1:

// do setup work 

myWaitHandle.Set(); 

線程2:

// do setup work 

myWaitHandle.WaitOne(); 

// this code will not continue until after the call to `Set` 
// in thread 1 completes. 

這是一個非常基本的例子,也有在網絡上提供他們的負荷。基本思想是WaitOne用於等待來自另一個線程的信號,表明發生了某種事情。在AsyncWaitHandle(通過異步調用委託返回)的情況下,WaitOne允許您導致當前線程等待異步操作完成。

如果未設置AutoResetEventManualResetEvent,則調用WaitOne將阻止調用線程,直到調用Set。這兩個類別的區別僅在於,在成功調用WaitOne完成後「解除」該事件,使得後續呼叫再次阻止,直到調用SetManualResetEvent必須通過調用Reset明確「取消」。

WaitAllWaitAnyWaitHandle類中的靜態方法,允許您指定要等待的數組WaitHandlesWaitAll將阻止,直到提供的手柄的所有Set,而WaitAny將只阻止直到其中一個其中獲得Set

1

這裏有一些很長的答案。對於任何尋找簡短答案的人:

等待句柄是使一個線程等待直到另一個線程到達某個點的機制。

您還可以有多個正在等待的線程和/或多個正在等待的線程,因此有WaitOne,WaitAllWaitAny方法。通過選擇這些類別中的一個,也可以使用多種語義選項:Mutex,Semaphore,ManualResetEvent,AutoResetEvent,這些都是有據可查的。