2010-01-29 26 views
4

我編寫了以下程序,用於使用升壓條件變量交替增加和加倍計數器(首先增加)。任何人都可以告訴我這是否是正確使用升壓條件變量。它工作正常。我不明白在等待函數調用中使用鎖。 condition.wait(鎖)是什麼意思?例如,在這個程序中增量和乘法使用兩個作用域鎖。我怎樣才能避免它們?這是一個正確使用boost條件變量嗎?

#include <boost/thread/thread.hpp> 
#include <boost/thread/mutex.hpp> 
#include <boost/bind.hpp> 
#include <boost/thread/locks.hpp> 
#include <boost/thread/condition_variable.hpp> 
#include <iostream> 
#include <stdlib.h> 
#include <time.h> 
using namespace std; 

int counter=0; 
boost::mutex m1,m2; 
bool incremented=false,multiplied=false; 
boost::condition_variable c1,c2; 
void Increment() 
{ 
    { 
     boost::mutex::scoped_lock lk(m1); 
     counter++; 
     incremented = true; 
     c1.notify_one(); 

     while(!multiplied) 
      c2.wait(lk); 
     multiplied=false; 

    } 
} 
void Multiply() 
{ 
    { 
     boost::mutex::scoped_lock lk(m2); 
     while(!incremented) 
      c1.wait(lk); 
     incremented = false; 
     counter = counter*2 ; 
     multiplied = true; 
     c2.notify_one(); 
    } 
} 

void IncrementNtimes(int n){ 

    for(int i=0;i<n;i++){ 
     Increment(); 
    } 
} 

void MultiplyNtimes(int n){ 

    for(int i=0;i<n;i++){ 
     Multiply(); 
    } 
} 
int main(int argc, char* argv[]) 
{ 
    srand (time(NULL)); 

    boost::thread thrd1(boost::bind(&IncrementNtimes,20)); 
    boost::thread thrd2(boost::bind(&MultiplyNtimes,20)); 
    thrd1.join(); 
    thrd2.join(); 
    cout<<"Main counter is:"<<counter<<endl; 
    return 0; 
} 

回答

18

不,這是不正確的。你幾乎在那裏,但最大的問題是,乘法和增量函數應該使用相同的互斥量。

互斥鎖是提供MUTual排除的對象。換句話說,互斥體的一點是防止兩個線程同時觸及相同的變量並導致不可預知的結果。互斥體就像一個線程一次所持有的一個令牌,它使它成爲訪問某個變量(或一組變量)的「正確」。在這種情況下,您嘗試保護的變量是counter。必須有一個且只有一個互斥體來控制訪問權counter。在你的情況下,每個線程都會擁有自己的令牌,它認爲它有權訪問計數器,所以會有不可預知的行爲。

你通過鎖定它「持有」一個互斥體。這就是鎖的要點,這就是爲什麼你不能「避免」它們。作用域鎖的整個點是,假設你只有一個互斥鎖m,當其中一個線程持有鎖m時,另一個線程保證不會在m上持有鎖。如果編碼正確,則在m上鎖定鎖應該是訪問counter的先決條件,因此counter的值應該是可預測的。

現在,關於wait()。對wait()的調用意味着「我放棄對該互斥鎖的鎖定,直到其他人發出這種情況的信號,然後我要它回來」。同時,線程停止。因此,假設你只有一個互斥體m和條件c,並lkm鎖,行c.wait(lk)意味着該線程將放棄鎖定lkm後停止執行,直到其他線程調用c.notify_one()(或c.notify_all()) 。當等待的線程從呼叫返回到wait()時,它將自動重新獲得的鎖lk,因此被允許再次訪問counter

最後,這些助推鎖是「範圍」鎖。這意味着它們會在銷燬時自動釋放(當它們超出範圍時)。因此,在這種情況下,每個函數都會保持其鎖定狀態,直到它退出爲止,除了放棄鎖定等待並暫停執行以等待信號時。

+0

非常感謝泰勒對此的解釋。 – Kamal 2010-01-29 20:51:32

2

條件變量必須始終與鎖關聯。要意識到的是,你必須持有鎖來調用wait(),但是一旦進入wait(),鎖就會被釋放。當你發出信號時,你還必須保持鎖定,最後等待不會再返回(即使在信號之後),直到信號員釋放鎖定。

4

由於條件變量是無狀態的,因此鎖與條件變量綁定 - 如果線程A在沒有服務員時發出信號,並且線程B進入等待狀態,則B不會被喚醒。出於這個原因,必須有一個狀態與條件變量相關聯(在這種情況下,增量和相乘)。鎖保護這個狀態在多個線程中被訪問。當您將鎖傳遞給wait()時,wait會自動釋放鎖並等待條件變量,並在等待返回時重新獲取鎖。這意味着沒有窗口的狀態變量後面的狀態可能已經改變並且等待發生。

例如,如果條件變量沒有拴鎖:

// In thread A 
while(!incremented) 

// Context switch to Thread B: 
incremented = true; 

// Context switch to Thread A:) 
    c1.wait(); // Whoops, waiting for a condition that already happened. 
+0

謝謝邁克爾。這真的有助於 – Kamal 2010-01-29 20:51:14

+0

+1,這就是要點! – 2013-09-08 01:42:06