2010-04-13 64 views
2

我有一個簡單的生產者/消費者場景,其中只有一件產品正在生產/消費。此外,生產者在繼續之前等待工作線程完成。我知道那種避免了多線程的整點,但請你只認爲它真正需要的是這樣的(:Monitor.Wait/Pulse的這種用法是否具有競爭條件?

此代碼不能編譯,但我希望你的想法:

// m_data is initially null 

// This could be called by any number of producer threads simultaneously 
void SetData(object foo) 
{ 
    lock(x)      // Line A 
    { 
     assert(m_data == null); 
     m_data = foo; 
     Monitor.Pulse(x)   // Line B 
     while(m_data != null) 
      Monitor.Wait(x)  // Line C 
    } 
} 

// This is only ever called by a single worker thread 
void UseData() 
{ 
    lock(x)      // Line D 
    { 
     while(m_data == null) 
      Monitor.Wait(x)  // Line E 
     // here, do something with m_data 
     m_data = null; 
     Monitor.Pulse(x)   // Line F 
    } 
} 

這裏是我不知道的情況:

假設多線程調用的SetData()與不同的輸入 只有其中一人將獲得鎖內,其餘的將被封鎖(A線) 假設裏面鎖套的那個m_data並且進入C線。

問題:線C上的Wait()是否允許線A上的另一個線程獲取該鎖並在工作線程甚至達到它之前覆蓋m_data

假設沒有發生,並且工作線程處理原始的m_data,並且最終到達F行,那麼當Pulse()熄滅時會發生什麼?

只有線C上等待的線程能夠獲得鎖嗎?或者它會與在線A上等待的所有其他線程競爭嗎?本質上,我想知道Pulse()/ Wait()是否特別相互「溝通」,或者它們與lock()處於同一級別。

這些問題的解決方案(如果存在的話)當然是顯而易見的 - 只需將SetData()與另一個鎖 - 比如lock(y)包圍即可。 我只是好奇,如果它甚至是一個問題開始。

回答

9

無法保證消費者會在另一個生產者之前排隊等候或就緒排隊。
C#和Java風格的監視器在Wikipedia, under "Implicit condition monitors"上描述。
Monitor會發生什麼很好的概述(從this優秀的網站採取的): alt text http://www.albahari.com/threading/waitpulse.png

「可以在C線的等待()允許在A線另一個線程來獲得鎖和前覆蓋M_DATA工作者的線程甚至可以達到它?「

假設SetData()由兩個生產者線程調用,P1 & P2
消費者線程C1也啓動。
P1,P2C1全部進入就緒隊列。
P1先取得鎖定。
等待隊列爲空,Pulse()line B沒有影響。
P1等待line C,所以它被放在等待隊列中。
就緒隊列中的下一個線程獲取該鎖。
它同樣可以是P2C1 - 在第一種情況下,斷言失敗。
你有一個競賽條件。

「假設沒有發生,並且工作線程處理原始m_data,並最終到達F行,那麼當Pulse()熄滅時會發生什麼?」

它會將服務員從等待隊列移動到就緒隊列。
鎖由發行Pulse()的線程保存。
通知線程得到一個機會在脈衝線程釋放鎖定(在就緒隊列中可能已經有其他人)後獲取鎖定。
MSDN, Monitor.Pulse()
「當前擁有指定對象上的鎖定的線程調用此方法來發出鎖定的下一個線程信號,接收到該脈衝後,等待的線程移至就緒隊列。調用Pulse的線程釋放鎖,就緒隊列中的下一個線程(不一定是脈衝線程)獲取鎖。「

「只有在C線上等待的線程能夠獲得鎖嗎?還是會與等待線A的所有其他線程競爭?

就緒隊列上的所有線程都「競爭」接下來的鎖。
無論他們是直接到達還是等待排隊,都可以使用Pulse()

「隊列」可能通過其他方式實現。 (不是隊列數據結構本身)。
這樣一個Monitor的實現可能不能保證公平 - 但可能有更高的整體吞吐量/性能。