2011-06-30 55 views
1

我有一些懷疑,這些mutexes足以確保下面的代碼示例的線程安全性,或者如果需要原子。總之問題是:idxActive會使得這個代碼線程不安全嗎?或者,即使原子線程不安全的代碼? :( 如果是重要的,我在32位x86,Linux上,GCC 4.6。當然,我推測,32位或64位是沒有差異,但如果有任何差異,我想知道32和64之間的位。互斥體+原子是否需要使此代碼線程安全,或者足夠互斥?

#include <memory> 
#include <boost/thread/thread.hpp> 
#include <string> 
#include <vector> 
#include <atomic> 
#include <boost/thread/mutex.hpp> 
using namespace std; 
using namespace boost; 
static const int N_DATA=2; 
class Logger 
{ 
    vector<string> data[N_DATA]; 
    atomic<int> idxActive; 
    mutex addMutex; 
    mutex printMutex; 
public: 
    Logger() 
    { 
     idxActive=0; 
     for (auto& elem: data) 
      elem.reserve(1024); 
    } 
private: 
    void switchDataUsed() 
    { 
     mutex::scoped_lock sl(addMutex); 
     idxActive.store((idxActive.load()+1)%N_DATA); 
    } 
public: 
    void addLog(const string& str) 
    { 
     mutex::scoped_lock sl(addMutex); 
     data[idxActive.load()].push_back(str); 
    } 
    void printCurrent() 
    { 
     mutex::scoped_lock sl(printMutex); 
     switchDataUsed(); 
     auto idxOld=(idxActive.load()+N_DATA-1)%N_DATA; //modulo -1 
     for (auto& elem:data[idxOld]) 
      cout<<elem<<endl; 
     data[idxOld].clear(); 
    } 
}; 
int main() 
{ 
    Logger log; 
    log.addLog(string("Hi")); 
    log.addLog(string("world")); 
    log.printCurrent(); 
    log.addLog(string("Hi")); 
    log.addLog(string("again")); 
    log.printCurrent(); 
    return 0; 
} 
+0

這是不是屬於上codereview.SE? –

+2

你知道你現在有std頭''和''? –

+0

@ Kerrek SB-是的,舊習慣很難消逝。 – NoSenseEtAl

回答

3

你並不需要使用原子變量,如果所有訪問這些變量由一個互斥保護。您的代碼就是這種情況,因爲所有公共成員函數在輸入時鎖定了addMutex。因此addIndex可以是一個普通的int,一切都會正常工作。互斥鎖定和解鎖確保正確的值對其他線程以正確的順序可見。

std::atomic<>允許外部一個互斥鎖的保護併發訪問,確保線程看到變量的正確的價值觀,甚至併發修改的面貌。如果您堅持默認的內存排序,它還可以確保每個線程讀取變量值最新的std::atomic<>可以用來編寫線程安全的算法不互斥,但如果所有的訪問都是由同一個互斥保護不是必需的。

重要更新

我只注意到你正在使用互斥:一個用於addLog,一個用於printCurrent。在這種情況下,你需要idxActive是原子,因爲單獨互斥不提供他們之間的同步。

+0

我不想粗魯,但你100%確定嗎?因爲就像我在給ChrisWue的評論中所說的,我不確定互斥確保更新的順序,AFAIK它只確保當時的最大1線程執行該部分代碼,而不是立即可見新值。我搜索了幾乎沒有發現的互斥體規範,以及我發現的 - 沒有提及原子性。 – NoSenseEtAl

+2

是的,我100%確定。互斥量保證在鎖定互斥鎖時對共享變量所做的任何更改對於下一個鎖定互斥鎖的線程都是可見的。如果情況並非如此,那麼它們的用處不大。 –

+0

好,很酷。再說一遍,這聽起來很傲慢,但是你是否有任何「嚴重」的鏈接(標準,gcc文檔......)指定了這一點。我嘗試MSDNing我的搜索和C#互斥體沒有運氣來確認你的oppinion。順便說一句,我的感覺是,你可能是對的,但當涉及到這樣的東西,我想100%肯定, – NoSenseEtAl

1

atomic是沒有直接關係的線程安全這只是確保了它的操作正是它說:。原子即使你所有的操作都是原子代碼不一定是線程安全的

在你的情況下,代碼應該是安全的,一次只能有一個線程可以輸入printCurrent()當這個函數被執行時,其他線程可以調用addLog()(但也只有一個在tim E)。取決於switchCurrent是否已被執行,這些條目將使其進入當前日誌,或者它們不會但在迭代它時不會輸入任何內容。每次只有1個線程可以輸入addLog,它與switchCurrent共享其互斥量,因此它們不能同時執行。

這會是這樣的,即使你做 idxActive一個簡單的INT MH,C++的內存模型只與單線程代碼交易 - 所以我也不太清楚,如果理論上可以打破它。我認爲如果你讓idxActive變得不穩定(基本上根本不允許任何加載/存儲優化),那麼對於所有實際的目的它都可以。或者,您可以從switchCurrent中刪除互斥鎖,但您需要保持原子的idxActive

作爲改善我先給switchCurrent返回舊指數,而不是重新計算它的。

+0

「即使您將idxActive設置爲簡單的int,情況也會如此。」但不能說不同的核心可以在緩存或寄存器中擁有不同版本的idxActive?因此,儘管互斥體保證只有一個人可以進入臨界區使得idxActive正則int可能會導致線程填充錯誤的數據向量?或者我錯了? – NoSenseEtAl

+0

@NoSenseEaAl:好點,更新我的回答 – ChrisWue