在我的程序中,我试图从多个线程更新一个值。我知道如何使用互斥(pthread_mutex_lock(), pthread_mutex_unlock()
)来做到这一点,但我刚刚学习了gcc原子内置,所以我想尝试一下。
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()
是一个返回布尔值的const函数。
这是正确的,还是在is_valid()
期间另一个线程可以更新A的值?
答案 0 :(得分:1)
我假设is_valid(A,B)可以根据A而改变。
在这种情况下,这是不安全的 - 请考虑以下顺序:
is_valid(A, B)
并获得真实is_valid(A, B)
并获得真实__sync_sub_and_fetch(A, B)
更改A,而新值A意味着is_valid(A, B)
现在为假。__sync_sub_and_fetch(A, B)
,即使它不应该因为is_valid(A, B)
现在为假。您可能对 compare-exchange 操作感兴趣。在这种情况下,您可以编写如下内容:
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,则在您忙于调用A
时,您知道没有其他人更改is_valid
。如果它返回false,那么你还没有真正做过任何事情,所以你可以自由地回去再试一次。
请注意is_valid
可能会多次调用;如果它有副作用(如在屏幕上打印消息),这是相关的。在这种情况下,您应该只使用互斥锁。
答案 1 :(得分:0)
这可能不是线程安全的,具体取决于is_valid究竟是什么。
特定内置点的目的只是执行原子减法。
考虑这段代码
A = A - B;
这不是线程安全的,因为它需要至少2个原子步骤 - A - B,然后分配回A。内置的这个解决了这个场景。