2012-03-01 86 views
3

我希望有人解決我對這個主題的困惑。聽起來很簡單,但我真的很困惑。使用互斥和信號處理共享數據

在生產者/消費者問題中,我使用了4信號量解決方案。我爲每個關鍵部分使用了不同的鎖。 說,

Pseudo code of producer: 
    wait(slot) // counting sem 
     wait(mutex1) // binary sem 
     rear <-- rear + 1 
     buffer[rear] <-- item 
     signal (mutex1) 
    signal(items) 

我在哪裏生產中使用的「互斥鎖2」作爲我的消費第二互斥,如「mutex1」。

現在,我的問題是。如果我的生產者和消費者沒有使用緩衝區(後部和前部),但使用堆棧,只有他們可以操作[頂部]。爲了確保互斥,我需要使用一個互斥鎖還是兩個不同的鎖?

Pseudo code of consumer with stack: 
     wait (message) 
      wait (mutex) 
       getspace <-- stack[top] 
       top – 1 
      signal (mutex) 
      signal (slot) 

就個人而言,我想我需要兩個步驟之一鎖,所以我要確保沒有任何的生產者和消費者訪問的最高兼任。但我不確定。 謝謝。

回答

2

我不是100%確定我遵循你的僞代碼,但我會盡我所能解釋如何使用信號量來管理生產者消費者進程中的堆棧。

如果您有一個跨多個線程訪問的堆棧,則需要在數據正在訪問時或者更具體地說是在被推送和彈出時鎖定它。 (這始終是生產者 - 消費者問題的一個基本假設。)

我們首先定義一個我們將用來鎖定堆棧的互斥鎖。

過程的全局聲明信號燈

stackAccessMutex = semaphore(1) # The "(1)" is the count 
           # initializer for the semaphore. 

接下來,我們需要將其鎖定,當我們添加或從它在我們的消費者和生產者線程刪除數據。

生產者線程

dataPushBuff #Buffer containing data to be pushed to the stack. 

…dataPushBuff is assigned… 

stackAccessMutex.wait() 
    stack.push(dataPushBuff) 
stackAccessMutex.signal() 

Consumer線程

dataRecvBuff = nil # Defining a variable to store the pushed 
        # content, accessible from only within 
        # the Consumer thread. 

stackAccessMutex.wait() 
    dataRecvBuff = stack.pop() 
stackAccessMutex.signal() 

…Consume dataRecvBuff as needed since it's removed from the stack… 

到目前爲止,一切都非常直截了當。生產者只會在需要時鎖定堆棧。消費者也是如此。我們應該不需要另一個信號量嗎?正確?沒有錯!

上面的場景使人產生了一個致命的假設 - 堆棧將在數據彈出之前始終使用數據進行初始化。如果消費者線程在生產者線程有機會彈出任何數據之前執行,您將在消費者線程中生成錯誤,因爲stack.pop()不會返回任何內容!爲了解決這個問題,我們需要告知消費者數據在堆棧中可用。

首先,我們需要定義一個信號量,它可以用來表示堆棧中的數據是否存在。流程信號燈的

全球宣言,版本#2

stackAccessMutex = semaphore(1) 
itemsInStack  = semaphore(0) 

我們初始化我們itemsInStack在我們的堆棧,這是0 項目的數量(參見圖1)

接下來,我們需要實現我們新的信號到我們的生產者和消費者線程。首先,我們需要讓生產者信號添加一個項目。我們現在更新制片人。

生產者線程版本#2

dataPushBuff 

…dataPushBuff is assigned… 

stackAccessMutex.wait() 
    stack.push(dataPushBuff) 
stackAccessMutex.signal() 
itemInStack.signal() #Signal the Consumer, we have data in the stack! 
        #Note, this call can be placed within the 
        #stackAccessMutex locking block, but it doesn't 
        #have to be there. As a matter of convention, any 
        #code that can be executed outside of a lock, 
        #should be executed outside of the lock. 

現在,我們可以檢查,看看是否有通過一個信號是在棧中的數據,讓我們重新寫我們的消費者線程。

Consumer線程版本#2

dataRecvBuff = nil # Defining a variable to store the pushed 
        # content, accessible from only within 
        # the Consumer thread. 

itemsInStack.wait() 
stackAccessMutex.wait() 
    dataRecvBuff = stack.pop() 
stackAccessMutex.signal() 

…Consume dataRecvBuff as needed since it's removed from the stack… 

...,就是這樣。正如你所看到的,有兩個信號量又都是強制性(見2),因爲我們需要鎖定我們的堆棧時,它的訪問我們需要表明我們的消費數據可用時並鎖定時,有沒有什麼堆棧。

希望這回答了你的問題。如果您有任何具體問題,我會更新我的回覆。

  1. 從理論上講,程序啓動時,你可以 預先初始化數據的籌碼。在這種情況下,您可以 應該 與 是 等於堆棧計值初始化itemsInStack信號。但是,在這個例子的情況下,我們 假設堆棧中沒有數據,也沒有任何數據初始化爲 。

  2. 值得一提的是,在一種情況下,您可以在理論上僅使用stackAccessMutex即可獲得 的具體情況。 考慮堆棧始終包含數據的情況。如果 堆棧是無限的,我們不需要告知我們的消費者已經添加了數據 ,因爲總會有數據。但是,在 的現實中,「無限堆棧」不存在。即使這應該是你的 當前環境中的情況下,有一個在加入 的itemsInStack信號的安全網的開銷。

    而且,它可能是很有誘惑力的拋出了itemsInStack下你目前的情況計算 信號,如果以 stack.pop()通話不會造成任何錯誤,如果它是不返回一個空棧上的任何 數據。

    這是合理的,但不推薦。假設消費者線程正在執行循環中的 代碼,則循環將連續執行堆棧消耗代碼,而 不會消耗任何數據。通過使用itemsInStack信號量,您正在暫停 線程,直到數據到達應該節省幾個CPU週期。