2015-05-14 49 views
-1

我今天需要做的與原子操作相當棘手,我想要一些幫助。概括地說,我想原子執行以下操作:比較一些原子交換其他

/* begin of atomicity */ 
if (--counter == 0) 
{ 
    Widget* widget = nullptr; 

    swap(some_widget, widget); 

    /* end of atomicity */ 

    // use local 'widget' here 
} 
/* end of atomicity */ 

我知道C++ 11個原子能和內部函數。前面的代碼只是,旨在顯示我想要以原子方式發生的事情。我知道一種解決方案是使計數器有一個「過渡」狀態(例如0xFFFFFFFF),並且只改變它的值首先移動到這個過渡,然後到一個新的值:它基本上類似於自旋鎖。任何想法如何有效地(即沒有螺旋鎖,沒有mutexes,可能沒有等待)?

非常感謝。

+0

有兩個**/* *結束的原子性\ */**,其中一個*真*結束?我懷疑有沒有辦法在原子中執行多個語句而不使用鎖定。 – davidshen84

+0

另外,你不想*刪除*'some_widget',你只是想把它分配給'nullptr'? – Barry

回答

0

假設C++ 11,你想做一個原子fetch_and_add,然後處理結果。

例如:

std::atomic<int> counter = 20; 

if (counter.fetch_sub(1) == 1) /* return value is old value */ 
{ 
    Widget* widget = nullptr; 
    swap(some_widget, widget); 
} 

如果在計數器跨線程使用,我們需要確保編譯器和硬件不弄亂內存排序。 std::atomic<>.fetch_sub()的默認排序是std::memory_order_seq_cst,這樣做會很好,但是如果您想贏得例如ARM64,你可以減少到std::memory_order_acquire

爲了使交換操作符合上述方案,我們確實需要一個鎖。 有趣的是,這適合於「雙重檢查鎖定」模式,這是一些人所不希望的,但在這裏非常有用 - 對於大多數操作你不會受到鎖的影響,但是當它重要時,它就在那裏。 這是如何:

std::atomic<int> counter = 20; 
std::mutex counterLock; 
std::atomic<Widget*> some_widget; 

... 

if (counter.load(std::memory_order_acquire) > 5) /* first check whether we're "close enough" */ 
{ 
    /* only lock when needed */ 
    std::lock_guard<std::mutex> templock(counterLock); /* free lock whenever we exit this scope */ 
    if (counter.fetch_sub(1) == 1) /* return value is old value */ 
    { 
    Widget* oldwidgetpr = some_widget.exchange(nullptr, std::memory_ordering_release); 
    /* do whatever you need to do with the oldwidgetptr */ 
    } 
} 
else 
    counter.fetch_sub(1); 
+0

這正是我現在的代碼,但減量和交換不是原子的。在遞減之後,其他一些線程可能會改變'some_widget'指針,並且我有一場比賽。 – keebus

+0

在這種情況下,爲什麼不去「雙重檢查鎖定」方法?它將允許在不鎖定的情況下快速遞減,但可以避免交換問題。我會編碼它,然後發佈它。 –

+0

編輯我的答案包括上述。 –