2011-11-18 30 views
2

我正在看這個msdn關於與Monitor.Pulse()的線程同步的頁面上的示例2。生產者 - 消費者示例,都在鎖(此)塊

Cell對象被創建並傳遞給生產者和消費者對象。

Cell cell = new Cell(); 
CellProd prod = new CellProd(cell, 20); 
CellCons cons = new CellCons(cell, 20); 

一個線程的每一個這兩個

Thread producer = new Thread(new ThreadStart(prod.ThreadRun)); 
Thread consumer = new Thread(new ThreadStart(cons.ThreadRun)); 

創建的ThreadRun在每種情況下一個循環,調用Cell.ReadFromCell()或Cell.WriteToCell(),取決於消費者/生產者。例如,製作者這

public void ThreadRun() 
{ 
    for(int looper=1; looper<=quantity; looper++) 
    cell.WriteToCell(looper); // "producing" 
} 

我不明白的一點是,在每一種方法,他們開始用

lock(this) 

而且由於它是一樣的Cell對象(即,上面的鎖定語句中的'this')傳遞給兩者,我一直以爲只有一個線程可以在這段代碼中。然而,從Monitor.Pulse()和Monitor.Wait()後面的代碼看來,這兩個線程同時在這些部分中。如果有人擁有該鎖,並且擊中了一個Monitor.Wait(),那麼另一個線程就永遠不會Pulse,因爲它被阻塞等待鎖定。

我猜測有一個簡單的解決方案,我誤解了一些東西,從我的測試運行代碼它看起來像兩個線程同時在他們的關鍵部分,所以鎖(這)不是做我應該做的印象。

+5

請注意,這是一種糟糕的編程習慣來「鎖定(this)」。相反,創建一個類型爲object的私有字段並對其進行鎖定。這是一個不好的做法,因爲*任何人*都可以鎖定該對象;如果您的鎖定訂單保證僅取決於*內部的代碼*可以鎖定的類型然後鎖定這會使代碼變得脆弱而難以分析。鎖定私鑰意味着你知道*只有*可以獲得對私鑰對象的引用的代碼才能取出鎖。 –

+2

Eric,我完全同意,但MSDN不應該首先提供這樣的示例代碼。大多數時候,人們使用'lock(this)'語句是因爲他們認爲如果它在MSDN上,它必須是正確的方法。有沒有辦法提交更新MSDN內容的請求? – SolutionYogi

+1

@SolutionYogi:好點。我會將其提請文檔管理員注意。謝謝! –

回答

8

你缺少的東西是Monitor.Wait(this)版本鎖在this直到它被喚醒。所以,是的,你的消費者線程看起來像就像它在鎖,因此擁有它,但它真的暫時釋放它。

the docs

當一個線程調用wait,它釋放對象的鎖並進入對象的等待隊列。對象就緒隊列中的下一個線程(如果有的話)獲取該鎖並獨佔使用該對象。所有調用Wait的線程都會保留在等待隊列中,直到它們從鎖的所有者發送的Pulse或PulseAll接收到一個信號。如果發送脈衝,則只有等待隊列頭部的線程受到影響。如果發送了PulseAll,則等待該對象的所有線程都會受到影響。當收到信號時,一個或多個線程離開等待隊列並進入就緒隊列。準備好的隊列中的線程被允許重新獲取鎖。

+0

Ahhh很有意義,謝謝! –