2014-08-29 40 views
0

我有一個線程,基本上做:volatile變量而不是互斥鎖變量來檢查改變的數據

int changed; //global variable 
.. 

for (;;) { 
    pthread_mutex_lock(&mtx); 
    if (changed) { 
     do_changes(); 
     changed = 0; 
    } 
    pthread_mutex_unlock(&mtx); 

    do_stuff(); 

} 

循環運行了幾十萬次的速率,而全球changed變量將設置很少(一天幾次)由另一個線程。

有了改變

volatile int changed; //global variable 
.. 

for (;;) { 

    if (changed) { 
     pthread_mutex_lock(&mtx); 
     do_changes(); 
     changed = 0; 
     pthread_mutex_unlock(&mtx); 
    } 
    do_stuff(); 
} 

我可以衡量這種方法,同時追求其價值循環的3-4%的性能提升。

然而,揮發性變量似乎非常令人沮喪。 這裏的方法有什麼缺點嗎?任何可能導致2.版本無法按預期工作的角落案例?

+0

您確定性能增加是因爲使用volatile而不是因爲do_stuff()(或其他代碼部分)在不同的運行中做了不同的事情嗎? – 2014-08-29 10:17:02

+1

如果循環僅由一個線程運行,則不會重新檢查鎖定後的值。考慮使用std :: atomic_flag來代替。 – firda 2014-08-29 10:21:08

+0

@KingsIndian是的,我確信這一點。 – user1255770 2014-08-29 10:52:09

回答

-1

爲了確保編譯器不會進行不需要的優化,但它不會使其成爲原子,這是一個好主意。

如果有更多線程正在讀取「已更改」,則可能會發生以下情況:當有另一個線程正在等待執行do_changes()時,一個線程將「更改」更新爲0 do_changes() 。

如果你想避免這一點,然後移動互斥鎖保護空間內的if語句。

希望這會有所幫助。

Carles。

+2

在多線程中使用'volatile'確實是一個糟糕的主意,不要那樣做。 – 2014-08-29 10:22:57

+2

儘管如此,但如果您知道架構*,並且您自己寫了「*但它並不會使其成爲原子」,那麼對於我們而言,「volatile」*是安全的,因此,那些downvoters可能無法正確讀取。 +1作爲平衡。 – firda 2014-08-29 11:01:22

+2

@firda volatile對於多線程來說既不安全也不足夠,它需要一位古魯級工程師來了解架構並正確使用它。即使這樣,使用'volatile'也會阻止編譯器進行優化,否則在使用原子時會進行優化。你可能喜歡看那個「原子武器」的視頻,這真的讓人大開眼界。 – 2014-08-29 11:13:01

2

volatile不會使您的變量線程安全或原子。您可能會喜歡使用C11 atomics

你基本上有兩個線程改變changed變量,從而覆蓋以前的值導致數據競爭。

我不能推薦足夠的觀看atomic Weapons: The C++ Memory Model and Modern Hardware。 (它也適用於C)。

+0

+10000000000000 – 2014-08-29 10:25:59

+0

沒有人在沒有鎖的情況下更改'changed'變量。請注意,更改後的變量只能在沒有鎖定的情況下讀取,不能寫入。它只會有2個重要值。 0或1.也就是說,它只是用來表示某些東西已經改變。檢查發現改變是否與其他線程改變它相同的迭代或下一次迭代無關。 – user1255770 2014-08-29 10:54:49

+0

@ user1255770請參閱https://en.wikipedia.org/wiki/Double-checked_locking – 2014-08-29 10:56:01

1

作者:然而,揮發性變量似乎非常令人沮喪。這裏的方法有什麼缺點嗎?任何可能導致2.版本無法按預期工作的角落案例?

首先:只要你想到使用volatile再想想,如果你不需要atomics現在看看會發生什麼:

1)如果你有多個線程的循環,這是不安全的。你能想到關於重複檢查:

if (changed) { // quick check 
    pthread_mutex_lock(&mtx); 
    if (changed) { // another thread could do the work 
     ... 

2)如果你的代碼是看它被改變,你需要使用原子能,因爲if(changed)之前pthread_mutex_lock可能無法看到,因爲緩存它是至關重要的。

3)其可以在x86(_64)與強存儲順序和原子INT工作訪問,但達不到其他架構。這就是爲什麼volatile不鼓勵,使用原子(並使其成爲習慣)的原因。 volatile不強制原子使用或任何其他同步。原子做(讀 - 修改 - 寫指令)。

std::atomic_flag validated; 
std::mutex mx; struct MyData { ... } data; 
void change() { 
    lock_guard<mutex> lock(mx); 
    data.something(); 
    validated.clear(); 
} 
void validate() { 
    if(!validated.test_and_set()) { 
     lock_guard<mutex> lock(mx); 
     data.update(); 
    } 
} 

注意:你永遠不會知道,如果除非你持有鎖,並使用另一個變量它的數據是有效還是無效。

4)只要盡你原來的代碼pthread_spinlock_t

5)小建議:不同步扮演上帝,除非你真的知道你在做什麼。您可以從互斥鎖切換到自旋鎖(由其他人編寫)並做一些基準測試。

關於編輯和評論:最初的答案只是從1)沒有任何前。事實證明,有些人既沒有閱讀完整的問題,也沒有閱讀完整的答案。皮蒂那些快速downvoters。這個網站不是臉書和那些票應該是電子郵件有幫助是沒有幫助的,這些up/downvotes是不喜歡在facebook上喜歡和不喜歡!我可能不同意其他答案的某些部分,但仍然認爲他們有幫助,雖然不完整(沒有提及完整的問題,但只是其中的一部分)或部分不符合(寫入相同的值沒有什麼不好如果我們知道我們可以做到這一點沒有問題,從許多線程變量)。

+0

正如書面答案似乎給_solutions_不是「顯示問題」,因爲它沒有寫成「你可能會考慮這樣做......但這不起作用,因爲......」。此外,(1)由於更多原因而不是緩存效果被破壞,參見http://www.aristeia.com/Papers/DDJ_Jul_Aug_2004_revised.pdf中的討論,指出「volatile」可能對線程間優化沒有影響 – 2014-08-29 12:29:04

+0

@JonathanWakely:作者的問題以*爲結尾?這裏的方法有什麼缺點嗎?任何可能導致2.版本無法按預期工作的角落案例?*我的答案也是這樣,一個可能出現的問題。我們不知道它是如何使用的,它實際上可以按預期工作,但需要滿足其他條件(單線程進行更新,不需要立即更新,只需一段時間)。 – firda 2014-08-29 12:33:19

1

如果您使用支持它們的C11環境,則可以使用atomic variables。如果你的系統支持它,他們使用特殊的指令來實現原子性,而不是鎖。如果你的系統不支持,他們使用鎖(標誌類型總是無鎖)。

如果您沒有C11,但您有兼容GCC的編譯器,請參閱sync系列函數。它與C11原子變量相似(但較老),但如果您的系統不支持它們,則會生成函數調用。

相關問題