2011-09-26 27 views
8

在某些地方,我看到有人創建線程池並創建線程並使用這些線程執行函數。當調用該函數boost :: mutex通過引用傳遞。爲什麼這樣做?我相信你可以在被調用的函數中聲明一個互斥體,或者聲明一個類成員或全局變量。任何人都可以解釋嗎?爲什麼要將互斥量作爲參數傳遞給線程所調用的函數?

例如

myclass::processData() 
    { 
     boost::threadpool::pool pool(2); 
     boost::mutex mutex; 

     for (int i =0; data<maxData; ++data) 
      pool.schedule(boost::bind(&myClass::getData, boost_cref(*this), boost::ref(mutex))); 
    } 

然後,

myClass::getData(boost::mutex& mutex) 
    { 
     boost::scoped_lock(mutex) // Why can't we have class member variable mutex or          
             //local mutex here 
     //Do somethign Here 
} 
+2

你應該添加一個特定的例子,在這裏使用它。這很可能是由於示例中實現的語義,並且根本沒有上下文就無法回答(除非你想通用*因爲*或*可能有意義*答案) –

回答

10

互斥鎖的是不可複製的對象,而他們可以是一個類的成員,這將大大複雜化父類的複製能力。因此,如果多個類實例需要共享相同的數據,則一種優選方法是將互斥體創建爲靜態數據成員。否則,如果互斥鎖只需要鎖定在類本身的一個實例中,就可以創建一個指向互斥鎖的指針作爲非靜態數據成員,然後該類的每個副本將擁有它自己的動態分配的互斥鎖如果這是一項要求,則保持可複製)。

在上面的代碼示例中,基本上發生的是全局互斥量通過引用傳遞到線程池中。這使得共享相同內存位置的所有線程都可以使用完全相同的互斥鎖在該內存上創建排它鎖,但不必管理互斥鎖本身的不可複製方面。此代碼示例中的互斥鎖也可以是類myClass的靜態數據成員,而不是通過引用傳入的全局互斥鎖,假設每個線程都需要鎖定可從每個線程全局訪問的某些內存。

本地互斥量的問題在於它只是一個本地可訪問的互斥量版本......因此,當線程鎖定互斥量以共享某些全局可訪問的數據時,數據本身不受保護,因爲每個其他線程將擁有自己的本地互斥鎖,可以鎖定和解鎖。它打敗了互相排斥的整個觀點。

+1

這是沒有意義的,有很多應用程序互斥體是非靜態成員的地方是合理的。比將互斥鎖作爲靜態成員更經常得多,並且沒有理由不將指針或引用存儲爲成員。互斥體應該處於受保護數據的級別(即,如果它保護靜態數據,它必須是靜態的,如果它保護成員屬性,那麼它可能應該是成員,如果它由不同的實例共享或不同類型,那麼它一定是更一般的 –

+0

對不起,我不是故意說你*不能*做到這一點...我只是說如果你讓它成爲一個非靜態的數據成員,創建一個正確的可複製類更難(最終它不會被複制,你將不得不存儲一個指向互斥體的指針,然後釋放並重新分配副本中的互斥體,這不是每個成員的「真實」副本我會修改我的答案,使其更清晰 – Jason

+2

......這一切都取決於對象的定義是什麼(語義上)在大多數情況下,互斥不構成對象狀態的一部分,但是一種實用工具,可以確保課堂的真實狀態不會在交際中被看到在多線程環境中使用時,iate不正確(不變式失效)狀態。 –

0

使用本地互斥是錯誤的:線程池可能會調用幾個函數實例,它們應該使用相同的互斥鎖。班級成員可以。將互斥鎖傳遞給該函數使其更具通用性和可讀性。調用者可以決定要傳遞哪個互斥體:類成員或其他任何東西。

+0

這讓我誤會了。如果這取決於調用類如何使用互斥鎖,它不應該不是接口定義的一部分? – kenny

1

我相信你可以在被調用的函數本身聲明一個互斥量,或者可以聲明一個互斥量類或全局類。任何人都可以解釋嗎?

在條目處創建一個新的互斥體並不會保護任何東西。如果你正在考慮聲明一個靜態(或全局)互斥體來保護非靜態成員,那麼你也可以把程序寫成一個單線程程序(好吧,有一些特例)。一個靜態鎖會阻塞除一個之外的所有線程(假設比賽);它相當於「一次最多隻能有一個線程在這個方法的內部運行」。聲明一個靜態互斥體來保護靜態數據沒有問題。正如David Rodriguez所說 - dribeas在另一個答案的評論中簡潔地寫道:「互斥體應該處於被保護的數據層面。」

可以宣佈每個實例的成員變量,它會使用一般形式:

class t_object { 
public: 
    ... 
    bool getData(t_data& outData) { 
     t_lock_scope lock(this->d_lock); 
     ... 
     outData.set(someValue); 
     return true; 
    } 

private: 
    t_lock d_lock; 
}; 

這種做法是好的,在某些情況下理想。在大多數情況下,當你構建一個系統時,這些系統有意從客戶端抽象鎖定機制和錯誤。一個缺點是可能需要更多的採購,而且通常需要更復雜的鎖定機制(例如可重入)。通過更多的收購:客戶可能知道一個實例只用於一個線程:爲什麼在這種情況下鎖定?以及一些小的線程安全方法會引入很多開銷。通過鎖定,您希望儘快進入和退出受保護區域(不需要引入許多采集),因此關鍵部分通常比典型的操作更大。

如果公共接口要求此鎖作爲參數(如圖中的例子),它是設計可通過在一個線程安全方式私有化鎖定(使目標函數,而不是傳遞被簡化的信號作爲外部資源的鎖)。

使用外部(或綁定或關聯)鎖,您可能會減少採集(或總時間鎖定)。這種方法還允許您在事實之後爲實例添加鎖定。它也允許客戶配置鎖的操作方式。客戶端可以通過共享它們(在一組實例中)使用更少的鎖。甚至組成一個簡單的例子可以說明這(支持這兩種模式):

class t_composition { 
public: 
    ... 
private: 
    t_lock d_lock; // << name and data can share this lock 
    t_string d_name; 
    t_data d_data; 
}; 

考慮一些多線程系統的複雜性,推動正確鎖定到客戶端的責任可以是非常糟糕的主意

這兩種模式(綁定和作爲成員變量)可以有效地使用。在特定情況下哪個更好,因問題而異。

+0

謝謝賈斯汀。但是,這是否意味着如果你想保護靜態數據,那麼你需要一個靜態的互斥量,並且如果你想保護數據成員,你需要類級互斥量作爲一般規則?作爲* general *方法的 – polapts

+0

:是的,這是確保您的實現是線程安全的另一種方法(在OP中)的多種可用方法之一。如果您希望最小化購置和鎖定時間,粒度也很重要。在某些情況下,級別鎖定可能太小,這會導致許多不必要的收購。在許多情況下,較大的(基於全球或基於文檔的)鎖定將不理想,因爲每次獲取鎖定的時間增加,其他人在此期間等待的可能性很高。 – justin

相關問題