为什么使用原子 CAS 的程序不能保持线程安全?
why the program using atomic CAS cannot keep thread safety?
int main(){
atomic<bool> atomic_lock(false);
std::atomic_flag lock_flag = ATOMIC_FLAG_INIT;
int count = 0;
auto f = [&](){
bool flag = false;
for( int i = 0; i< 10000000; ++i){
while(!atomic_lock.compare_exchange_strong(flag, true)){}
//while(lock_flag.test_and_set(std::memory_order_seq_cst));
++count;
//lock_flag.clear(std::memory_order_seq_cst);
atomic_lock.store(false, std::memory_order_seq_cst);
}
};
thread t1(f);
thread t2(f);
t1.join();
t2.join();
cout<<count<<endl;
return 0;
}
这是我的程序,我想用CAS替换mutex,但是输出不是20000000,说明不是线程安全程序,哪里错了?但是,我用 atomic_flag 替换 atomic 显示如上,输出是正确的
您忘记在每次 CAS 尝试之前设置 flag = false
以确保您仅在 CAS 从 false 变为 true 时才成功。
请记住,在 CAS 失败时,“预期”(旧值第一个 arg)会更新为 flag
的当前值;这就是它引用它的原因 (cppref). See also Understanding std::atomic::compare_exchange_weak() in C++11.
每次失败后,您的代码都会循环,直到它可以从上一次迭代看到的任何内容进行 CAS。这很容易成功,您很快就会在临界区内拥有两个线程立即创建数据争用 UB(这会在多核系统上导致真正的问题,如果增量未编译为一条指令,甚至在单核系统上也会导致问题。)
对于 C++ std::atomic:CAS 来说,通过指针而不是非 const
引用来获取该 arg 可能是一个更好的设计,例如匹配的 non-member std::atomic_compare_exchange_weak(std::atomic<T>* obj, T* expected, T desired)
C11接口。或许不是,但它可以避免这个隐藏的陷阱。
int main(){
atomic<bool> atomic_lock(false);
std::atomic_flag lock_flag = ATOMIC_FLAG_INIT;
int count = 0;
auto f = [&](){
bool flag = false;
for( int i = 0; i< 10000000; ++i){
while(!atomic_lock.compare_exchange_strong(flag, true)){}
//while(lock_flag.test_and_set(std::memory_order_seq_cst));
++count;
//lock_flag.clear(std::memory_order_seq_cst);
atomic_lock.store(false, std::memory_order_seq_cst);
}
};
thread t1(f);
thread t2(f);
t1.join();
t2.join();
cout<<count<<endl;
return 0;
}
这是我的程序,我想用CAS替换mutex,但是输出不是20000000,说明不是线程安全程序,哪里错了?但是,我用 atomic_flag 替换 atomic 显示如上,输出是正确的
您忘记在每次 CAS 尝试之前设置 flag = false
以确保您仅在 CAS 从 false 变为 true 时才成功。
请记住,在 CAS 失败时,“预期”(旧值第一个 arg)会更新为 flag
的当前值;这就是它引用它的原因 (cppref). See also Understanding std::atomic::compare_exchange_weak() in C++11.
每次失败后,您的代码都会循环,直到它可以从上一次迭代看到的任何内容进行 CAS。这很容易成功,您很快就会在临界区内拥有两个线程立即创建数据争用 UB(这会在多核系统上导致真正的问题,如果增量未编译为一条指令,甚至在单核系统上也会导致问题。)
对于 C++ std::atomic:CAS 来说,通过指针而不是非 const
引用来获取该 arg 可能是一个更好的设计,例如匹配的 non-member std::atomic_compare_exchange_weak(std::atomic<T>* obj, T* expected, T desired)
C11接口。或许不是,但它可以避免这个隐藏的陷阱。