2012-02-16 76 views
5

我是多線程編程的新手,對Mutex的工作原理感到困惑。在Boost :: Thread手冊中,它指出:加速,互斥概念

Mutexes保證只有一個線程可以鎖定給定的互斥鎖。如果代碼段被互斥體鎖定和解鎖包圍,則可以確保一次只有線程執行該代碼段。當該線程解鎖互斥量,其他線程可以進入到該代碼區域:

我的理解是互斥是用來保護的代碼段從在同一時間被多個線程執行,保護一個變量的內存地址。我很難理解這個概念,如果我有兩個不同的函數試圖寫入同一個內存地址,會發生什麼。

有像這樣在Boost庫:

  1. 鎖變量的存儲器地址,例如,雙X,鎖(X);所以 其他具有不同函數的線程不能寫入x。
  2. 用x做某事,例如x = x + rand();
  3. 解鎖(x)

謝謝。

+1

我想你可能對[軟件事務內存](http://en.wikipedia.org/wiki/Software_transactional_memory)感興趣,但在C++中並不存在(本地)。 – 2012-02-16 22:07:25

回答

5

互斥體本身只確保只有一個執行線程可以在任何給定時間鎖定互斥鎖。您需要確保只有在互斥鎖被鎖定時纔會修改相關變量。

C++的確給你一種方法來做到這一點比在類似的東西更容易C.在C語言中,編寫代碼非常重要,確保在修改變量的任何位置,首先鎖定互斥鎖(當然,當你完成時,解鎖它)。

在C++中,這是很容易把它全部用一些運算符重載封裝成一個類:

class protected_int { 
    int value; // this is the value we're going to share between threads 
    mutex m; 
public: 
    operator int() { return value; } // we'll assume no lock needed to read 
    protected_int &operator=(int new_value) { 
     lock(m); 
     value = new_value; 
     unlock(m); 
     return *this; 
    } 
}; 

很顯然,我簡化了很多(到如此地步,它可能是無用的,因爲它代表),但希望你能得到這個想法,那就是大多數代碼只是把對象看作是一個普通變量。

但是,當您這樣做時,每次爲其分配值時都會自動鎖定互斥鎖,並在此後立即解鎖。當然,這幾乎是最簡單的情況 - 在許多情況下,您需要執行一些操作,例如鎖定互斥鎖,同時修改兩個(或更多)變量,然後解鎖。然而,不管複雜程度如何,這個想法仍然是你將所有修改過的代碼集中在一個地方,所以你不必擔心在其他代碼中鎖定互斥體。如果你的確有兩個或更多的變量,那麼你通常必須鎖定互斥體才能讀取,而不僅僅是寫入 - 否則,如果其中一個變量已被修改,但另一個變量卻沒有被修改,噸。

1

爲了保護多個線程在兩個不同的功能共享的內存地址,這兩個函數必須使用同一互斥 ...否則你會碰到這樣一個場景,在任一功能的線程可以不加區別地訪問相同「受保護」的內存區域。

因此boost::mutex適用於您描述的場景,但您必須確保對於您正在保護的給定資源,所有到該資源的路徑都會鎖定同一個對象實例。

3

不,沒有任何東西在助推(或別處)會鎖定那樣的內存。 你必須保護訪問你想保護的內存的代碼。

如果我有2個不同的功能試圖寫入相同的 內存地址,會發生什麼情況。

假設你的意思是2個功能不同的線程中執行,這兩個函數應該鎖定相同互斥,所以只有其中一個線程可在給定時間寫入變量。

訪問(讀取或寫入)相同變量的任何其他代碼也必須鎖定相同的互斥鎖,否則將導致不確定行爲。

0

你對互斥的理解是正確的。它們保護鎖定和解鎖之間的代碼段。

根據兩個線程寫入內存相同位置時發生的情況,它們將被序列化。一個線程寫入其值,另一個線程寫入它的值。這個問題是你不知道哪個線程會先寫(或最後),所以代碼不是確定性的。

最後,爲了保護變量本身,您可以在原子變量中找到近似的概念。原子變量是受編譯器或硬件保護的變量,可以自動修改。也就是說,你評論的三個階段(讀,修改,寫)發生在原子上。看看Boost atomic_count

1

我認爲你錯過的細節是「代碼部分」是代碼的任意部分。它可以是兩種功能,一種功能,一種線路,或其他。

所以你的2個不同的功能時,他們訪問共享數據保持相同的互斥的部分,「一個代碼段由一個互斥鎖定和解鎖包圍」所以因此「它保證僅在一個線程時間執行該代碼段「。

此外,這是解釋互斥鎖的一個屬性。它並沒有聲稱這是他們擁有的唯一財產。

2

可以使用Boost.Atomic對某些類型的非阻塞原子操作進行操作。這些操作是非阻塞的,通常比互斥鎖快得多。例如,要添加一些原子可以這樣做:

boost::atomic<int> n = 10; 
n.fetch_add(5, boost:memory_order_acq_rel); 

此代碼原子增加了5〜n