c++ - 为什么使用原子 CAS 的程序不能保持线程安全?
问题描述
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 替换为 atomic_flag 显示如上,输出是正确的
解决方案
您忘记flag = false
在每次 CAS 尝试之前进行设置,以确保您只有在 CAS 从 false 变为 true 时才成功。
请记住,在 CAS 失败时,“预期”(旧值第一个 arg)会更新为 ; 的当前值flag
。这就是为什么它通过引用(cppref)来获取它。另请参阅了解 C++11 中的 std::atomic::compare_exchange_weak()。
每次失败后,您的代码都会循环,直到它可以从上次迭代中看到的任何内容进行 CAS。 这很容易成功,并且您将很快在关键部分中同时拥有两个线程,创建数据竞争 UB(这会在多核系统上引起真正的问题,如果增量无法编译,甚至在单核系统上也会导致一条指令。)
const
对于 C++ std::atomic:CAS 来说,通过指针而不是非引用来获取该 arg 可能是一个更好的设计,就像匹配 C11 接口的非成员一样。 std::atomic_compare_exchange_weak(std::atomic<T>* obj, T* expected, T desired)
或许不是,但它会避免这个隐藏的陷阱。
推荐阅读
- odata - 在获取实体操作中使用筛选查询的逻辑应用
- windows - Docker 构建失败并出现错误:无法注册层:重新执行错误:退出状态 1
- angular - 多租户和 AWS Amplify、AppSync、Cognito
- bash - bash: /home/user/.bash_completion/alacritty: 没有这样的文件或目录
- python-3.x - Python。按日期过滤数组的最佳方法
- python - 从 S3 存储桶读取熊猫数据的最快选择?
- pytorch - 与 Pytorch 中从列表到张量的转换相关的错误
- powerbi - 是否可以在 Power BI 中创建此仪表板?
- c# - XmlDocument loping (foreach) reomving NameSpace "xmlns"
- python - 如何使用临时文件为 MySQL 数据加速 pandas read_sql?