2012-08-25 26 views
5

http://www.codeproject.com/Articles/28785/Thread-synchronization-Wait-and-Pulse-demystifiedMonitor.Pulse與等待 - 意外的行爲

隊列:

就緒隊列是線程正在等待一個 特定的鎖集合。 Monitor.Wait方法引入了另一個隊列: 等待隊列。這是必需的,因爲等待脈衝不同於等待獲取鎖定 。像就緒隊列一樣,等待的隊列是 。

推薦模式:

這些隊列可能會導致意外的行爲。當發生脈衝時,等待隊列的頭部被釋放,並被添加到準備好的 隊列中。但是,如果就緒隊列中有其他線程,它們將在發佈的線程之前獲取該鎖。這是一個 問題,因爲獲取該鎖的線程可能會改變脈衝線程所依賴的狀態 。解決辦法是在鎖定語句內使用 條件

* Q =隊列。

由此我明白,當我打電話給Pulse時,它會在結束之前做兩件事情。首先,它將一個線程從等待中的Q中移除到準備好的Q上。其次,它讓Ready Q中的一個線程(不知道該線程是誰)獲取鎖定;它並不關心誰獲取鎖(來自等待Q的線程或由於某種原因處於準備好Q的線程)。

如果我是正確的關於然後爲什麼是把一個whileMonitor.Wait幫助解決這個問題(問題 - 脈衝結束即使從等待Q來到線程沒有獲取鎖)?

A告訴我,如果我對Monitor.Pulse的目的是正確的。

B爲什麼我需要把下面的答案的whileMonitor.Wait

的完整代碼:

class Program 
{ 
    static Queue<int> queue = new Queue<int>(); 
    static object someMonitor = new object(); 

    static void Main(string[] args) 
    { 
     Thread Thread1 = new Thread(WorkAlltheTime); 
     Thread1.Name = "Thread1"; 
     Thread Thread2 = new Thread(WorkAlltheTime); 
     Thread2.Name = "Thread2"; 
     Thread Thread3 = new Thread(WorkOnce); 
     Thread3.Name = "Thread3"; 
     Thread1.Start(); 
     Thread2.Start(); 
     Thread.Sleep(1000); 
     Thread3.Start(); 
     Console.ReadLine(); 
    } 

    static void WorkAlltheTime() 
    { 
     Console.WriteLine("Came in to Ready Q: " + Thread.CurrentThread.Name); 
     lock (someMonitor) 
     { 
      Console.WriteLine("Came out from Ready Q: " + Thread.CurrentThread.Name); 
      // Broken! 
      while (queue.Count == 0) 
      { 
       Console.WriteLine("Came in to Waiting Q: " + Thread.CurrentThread.Name); 
       Monitor.Wait(someMonitor); 
       Console.WriteLine("Came out from Waiting Q: " + Thread.CurrentThread.Name); 
      } 
      queue.Dequeue(); 
      Console.WriteLine("Thread: "+Thread.CurrentThread.Name+" Pulled Out"); 
     } 
    } 

    static void WorkOnce() 
    { 
     lock (someMonitor) 
     { 
      queue.Enqueue(1); 
      Monitor.Pulse(someMonitor); 
     } 
    } 
} 

回答

9

想象你正在試圖寫一個生產者/消費者隊列 - 每次產生一次Pulse物品和消費者需要等待,直到有物品消耗。你會寫這樣的代碼:

Foo item; 
lock(someMonitor) 
{ 
    while (queue.Count == 0) 
    { 
     Monitor.Wait(someMonitor); 
    } 
    item = queue.Dequeue(); 
} 
// Use the item 

假設你沒有有while循環,而是寫道:

Foo item; 
lock(someMonitor) 
{ 
    // Broken! 
    if (queue.Count == 0) 
    { 
     Monitor.Wait(someMonitor); 
    } 
    item = queue.Dequeue(); 
} 
// Use the item 

現在假設你有一個線程已經在等待,然後又線程就在鎖定語句之前......然後一個生產者脈衝監視器(當然,將一個項目添加到隊列中)。

在這一點上,完全可行的是,那些尚未鎖定的線程將首先獲得鎖定......在「等待」線程獲得鎖定的時候,隊列將再次爲空。只用一個if語句,如果沒有循環,當隊列爲空時,最終會出隊,否則會失敗。

使用while循環,您將再次等待,直到生成下一個項目,這就是您真正想要的。

+0

謝謝你,我寫了你的例子的完整代碼,並用你的解釋我測試了90次,現在我完全理解了。謝謝Agian! –