2016-12-07 30 views
1

在我的程序中,我試圖從多個線程更新一個值。我知道如何使用互斥體(pthread_mutex_lock(), pthread_mutex_unlock())來做到這一點,但我剛剛瞭解了gcc的原子構建,所以我想嘗試一下。如何使用gcc atomic builtins?

shared value A; 
void each_working_thread() { 
    thread local variable B; 
    if (is_valid(A,B)) 
     __sync_sub_and_fetch(A,B); 
    else 
     throw error; 
} 

其中is_valid()是返回布爾型的常量函數。

這是否正確,或者在is_valid()期間A的值可能被另一個線程更新?

回答

1

我認爲is_valid(A,B)可根據A.改變

在這種情況下,這是不是安全的 - 考慮以下序列:

  • 線程1調用is_valid(A, B),並得到真正的
  • 線程2調用is_valid(A, B)並且變爲真
  • 線程1調用__sync_sub_and_fetch(A, B)變化A,並且A的新值表示is_valid(A, B)現在是錯誤的。
  • 線程2調用__sync_sub_and_fetch(A, B)即使它不應該因爲is_valid(A, B)現在是錯誤的。

您可能會感興趣的比較交換操作。在這種情況下,你可以寫這樣的:

int oldValueOfA, newValueOfA; 
do { 
    oldValueOfA = A; 
    __sync_synchronize(); // memory barrier; makes sure A doesn't get accessed after this line 
    if (!is_valid(oldValueOfA, B)) 
     throw error; 
    newValueOfA = oldValueOfA - B; 
} while(!__sync_bool_compare_and_swap(&A, oldValueOfA, newValueOfA)); 

你不能讓兩個操作原子,但你可以檢測如果他們不是原子然後你可以再次嘗試。

這是有效的,因爲如果A仍然不包含oldValueOfA,__sync_bool_compare_and_swap什麼也不做,並返回false。否則,它將其設置爲newValueOfA並返回true。因此,如果它返回true,則在您忙於撥打is_valid時,您知道其他人沒有更改A。如果它返回false,那麼你還沒有做任何事情,所以你可以自由地回去重試。

請注意is_valid可能會被調用幾次;這是相關的,如果它有副作用(如在屏幕上打印消息)。在這種情況下,你應該只使用一個互斥量。

+0

感謝您的解釋。所以在這種情況下我必須使用互斥鎖?如果'is_valid'只是'return A> B;'或者是一個相當複雜的函數,需要數百條指令,那麼有什麼區別嗎? – bbvan

+0

@bbvan如果'is_valid'只是'return A> B;',你認爲我描述的東西還是可以發生的嗎?如果不是,爲什麼不呢?事實上,你問*建議*你沒有真正想過我的解釋。 – immibis

+0

是的,我認爲沒有區別...但是我希望有一些像'sync_if_cmpgt_then_sub'這樣的神奇的原子操作... – bbvan

0

這可能不是線程安全的,具體取決於is_valid究竟是什麼。

該具體內建的點只是執行原子扣除。

考慮以下代碼

A = A - B; 

,因爲它需要至少2個原子階段這不是線程安全的 - 在A - B,然後指派回A.這樣做的內置解決了一個場景。