2012-03-06 56 views
4

爲了學習boost :: thread的combinatorics,我爲鎖定一個普通的互斥體(M)的線程實現了一個簡單的屏障(BR)。然而,據我所知,當進入BR.wait()時,互斥鎖沒有被釋放,所以爲了讓所有線程到達BR,需要執行M上的鎖的手動釋放。所以,我有以下代碼:手動釋放增強鎖定?

boost::barrier BR(3); 
boost::mutex M; 

void THfoo(int m){ 
    cout<<"TH"<<m<<" started and attempts locking M\n"; 
    boost::lock_guard<boost::mutex> ownlock(M); 

    cout<<"TH"<<m<<" locked mutex\n"; 
    Wait_(15); //simple wait for few milliseconds 

    M.unlock(); //probably bad idea 
    //boost::lock_guard<boost::mutex> ~ownlock(M); 
    // this TH needs to unlock the mutex before going to barrier BR 

    cout<<"TH"<<m<<" unlocked mutex\n"; 
    cout<<"TH"<<m<<" going to BR\n"; 
    BR.wait(); 
    cout<<"TH"<<m<<" let loose from BR\n"; 
} 

int main() 
{ 
    boost::thread TH1(THfoo,1); 
    boost::thread TH2(THfoo,2); 
    boost::thread TH3(THfoo,3); 

    TH2.join(); //but TH2 might end before TH1, and so destroy BR and M 
    cout<<"exiting main TH \n"; 

    return 0; 
} 

而M.unlock()顯然是一個壞的解決方案(不使用鎖定);那麼如何(簡單地)釋放鎖?另外:我如何(正確)在main()中等待所有線程完成? (TH2.join()不好,因爲TH2可能會先完成...);

請不要建議復飛,例如,帶有條件變量,我也可以使用它,但是必須可以在沒有它們的情況下直接執行。

回答

4

喜歡的東西:

void THfoo(int m){ 
    // use a scope here, this means that the lock_guard will be destroyed (and therefore mutex unlocked on exiting this scope 
    { 
    cout<<"TH"<<m<<" started and attempts locking M\n"; 
    boost::lock_guard<boost::mutex> ownlock(M); 

    cout<<"TH"<<m<<" locked mutex\n"; 
    Wait_(15); //simple wait for few milliseconds 

    } 
    // This is outside of the lock 
    cout<<"TH"<<m<<" unlocked mutex\n"; 
    cout<<"TH"<<m<<" going to BR\n"; 
    BR.wait(); 
    cout<<"TH"<<m<<" let loose from BR\n"; 
} 

至於等待,只需調用加入的所有線程句柄(如果他們已經完成,函數將立即返回)

+0

很酷,謝謝... – 2012-03-06 10:45:39

1

讓它走出去的範圍:

void THfoo(int m){ 
    cout<<"TH"<<m<<" started and attempts locking M\n"; 
    { 
     boost::lock_guard<boost::mutex> ownlock(M); 

     cout<<"TH"<<m<<" locked mutex\n"; 
     Wait_(15); //simple wait for few milliseconds 
    } 

    // this TH needs to unlock the mutex before going to barrier BR 

    cout<<"TH"<<m<<" unlocked mutex\n"; 
    cout<<"TH"<<m<<" going to BR\n"; 
    BR.wait(); 
    cout<<"TH"<<m<<" let loose from BR\n"; 
} 
1
cout<<"TH"<<m<<" started and attempts locking M\n"; 
{ 
    boost::lock_guard<boost::mutex> ownlock(M); 

    cout<<"TH"<<m<<" locked mutex\n"; 
    Wait_(15); //simple wait for few milliseconds 

} //boost::lock_guard<boost::mutex> ~ownlock(M); 
// this TH needs to unlock the mutex before going to barrier BR 

cout<<"TH"<<m<<" unlocked mutex\n"; 

只要你join所有的線程,TH2完成第一個問題的唯一問題是,TH1必須完成運行之前,TH2可以「收穫」join,任何剩餘的資源,如返回值釋放。 3線程並不值得擔心。如果該內存使用是一個問題,那麼你可以使用timed_join反覆嘗試所有線程。

你也可以做你不想要的東西 - 讓主線程等待一個條件變量,並且每個線程完成時在某個地方存儲一個值,表示它已完成,併發信號通知條件變量,線程可以join吧。你必須絕對確定線程將發出信號,否則你可能會永遠等待它。所以如果你取消線程,請小心。

+0

我很擔心M和BR,在main()退出之後,其他TH不能訪問。所以,問題是如何正確地做到這一點,以避免崩潰的風險 - 我想所有線程上的「timed_join」都是答案,對吧? – 2012-03-06 10:48:20

+0

@PMarecki:除非你能想到一個特殊的原因,否則只要執行'TH1.join(); TH2.join(); TH3.join();'。然後'main'不會在任何線程運行時返回(儘管它可能會拋出)。你應該計劃加入或分離你開始的每一個線程。 'boost :: thread'的析構函數在任何情況下都會將它們分開,我認爲這意味着它們應該在全局變爲被銷燬之前退出。但是,如果你看到其他,也許我錯了。 – 2012-03-06 10:51:19

1

如果你使用boost :: mutex :: scoped_lock而不是boost :: lock_guard,它有一個unlock()方法。如果你調用它,那麼鎖將不會嘗試在其析構函數中重新解鎖。我發現代碼更符合我的口味,而不是將鎖放在自己的區塊中。

+0

要添加一點:如果您在底層互斥體上調用unlock(),就像您在問題中所做的那樣,lock_guard超出範圍時將再次調用unlock()。如果你使用scoped_lock(或類似的)並調用unlock(),但它不會在第二次超出範圍時嘗試解鎖。 – RobH 2012-03-06 10:44:15

+0

我發現塊(和伴隨的縮進)一個_lot_清晰比交錯功能調用 – 2012-03-06 12:16:26

8

除了塊作用域的boost::lock_guard,你也可以使用一個boost::unique_lock可以是unlock()「明確ED:

boost::unique_lock<boost::mutex> ownlock(M); 

cout<<"TH"<<m<<" locked mutex\n"; 
Wait_(15); //simple wait for few milliseconds 

ownlock.unlock(); 

如果以後需要重新獲得它之前釋放互斥這是非常有用的。

至於加入,只需依次在所有線程句柄上調用join()

+1

感謝您對unique_lock的評論。不勝感激! – 2012-03-06 10:56:27