2013-10-09 110 views
2

wikipedia中提及的生產者消費者問題的「執行不力」的僞代碼如下。據說這種解決方案具有可能導致死鎖的競態條件。多線程編程 - 生產者消費者

我的問題是:不只是修改喚醒其他線程的條件如下解決了可能的死鎖問題。這樣就不會有一個可能會丟失的喚醒,但隨後會有多個喚醒,或者我錯過了一些東西。嘗試在這裏瞭解。

int itemCount = 0; 

procedure producer() { 
    while (true) { 
     item = produceItem(); 

     if (itemCount == BUFFER_SIZE) { 
      sleep(); 
     } 

     putItemIntoBuffer(item); 
     itemCount = itemCount + 1; 

     //if (itemCount == 1) <<<<<<<< change this to below condition 
     if(itemCount > 0) 
     { 
      wakeup(consumer); 
     } 
    } 
} 

procedure consumer() { 
    while (true) { 

     if (itemCount == 0) { 
      sleep(); 
     } 

     item = removeItemFromBuffer(); 
     itemCount = itemCount - 1; 

     //if (itemCount == BUFFER_SIZE - 1) <<<<<<< Change this to below 
     if(itermCount < BUFFER_SIZE) 
     { 
      wakeup(producer); 
     } 

     consumeItem(item); 
    } 
} 

回答

1

將不僅僅改變了wakeing其他線程如下解決可能的死鎖問題的條件。

不,競態條件依然存在。問題是有多個線程正在進行消費和/或生產。當消費者(例如)被喚醒並被告知有待處理的項目時,它可能會刪除該項目,但其他一些線程(或多個線程)已經到達那裏。

解決的辦法是做到以下幾點:

lock() { 
    while (itemCount == 0) { 
     sleep(); 
    } 

    item = removeItemFromBuffer(); 
    itemCount = itemCount - 1; 
} 

因此,即使消費者被喚醒,它會立即再次檢查itemCount不爲0與while循環。即使itemCount增加了,另一個線程可能已經刪除了該元素,並在之前遞減itemCount,得到該信號的線程有機會採取行動。這就是比賽。

生產者方面也是如此,雖然種族是阻止生產者過度填充緩衝區。生產者可能會因爲有可用空間而被喚醒,但到了將物品放入緩衝區的時候,其他線程會打敗它並重新填充緩衝區。在它被喚醒之後,它必須再次測試以確保有空間。

我從本網站的網站上一行一行的詳細說明了這場比賽,標題爲Producer Consumer Thread Race Conditions。這裏還有一個小測試程序來演示這個問題。

要實現的重要一點是,在大多數鎖定實現中,有一個線程隊列等待訪問鎖。當一個信號發送到一個線程時,它首先必須重新獲取的鎖,之前的它可以繼續。當一個線程被髮信號時,它將進入BLOCK隊列的。如果有額外的線程正在等待鎖定,但是不是正在等待,它們將在喚醒線程之前運行並竊取項目。

這與關於類似代碼中的while循環的這個問題非常相似。不幸的是,接受的答案並不能解決這種競爭條件。請考慮加強my answer to a similar question here。虛假喚醒的一個問題,但這裏真正的問題是維基百科討論的競爭條件。

+1

也修改'itemCount'的所有行都需要是原子的或鎖定的。 – Adam

+0

剛修復@Adam。謝謝。 – Gray

+0

在多個消費者生產者的情況下不應該將喚醒發送到所有線程?那樣可以避免比賽? – goldenmean