c++ - 为什么使用原子 CAS 的程序不能保持线程安全?

标签 c++ c++11 thread-safety stdatomic

<分区>


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 显示如上,输出是正确的

最佳答案

你忘了设置 flag = false在每次 CAS 尝试之前确保您只有在 CAS 从假变为真时才成功。

请记住,在 CAS 失败时,“预期”(旧值第一个 arg)会更新为当前值 flag ;这就是它引用它的原因 ( cppref )。另见 Understanding std::atomic::compare_exchange_weak() in C++11 .

每次失败后,您的代码都会循环,直到它可以从上次迭代看到的任何内容进行 CAS。这很容易成功,您将很快在关键部分内同时拥有两个线程,创建数据-race UB(这会在多核系统上导致真正的问题,如果增量没有编译成一条指令,甚至在单核系统上也会导致问题。)


对于 C++ std::atomic:CAS 来说,通过指针而不是非 const 获取该 arg 可能是一个更好的设计引用,比如non-member std::atomic_compare_exchange_weak(std::atomic<T>* obj, T* expected, T desired)与C11接口(interface)相匹配。或者可能不会,但它可以避免这个隐藏的陷阱。

关于c++ - 为什么使用原子 CAS 的程序不能保持线程安全?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62945850/

相关文章:

c++ - 如何使用Crypto++并为RSA返回可打印的字节/字符数组?

C++:为什么空格在读取时总是终止字符串?

c++ - 为什么我得到 "error: ‘apply’ is not member of ‘std’”?

sorting - C++ std::const 结构的排序

c++ - 具有动态分配内存和 mmap 内存的 shared_ptr

c++ - libstdc++ 中的 ostream operator<< 是线程敌对的吗?

c++ - vector 如何找到第一个和最后一个当前值

c - 优雅地(即最终合作地)暂停线程执行

c# - 是否在 ASP.Net 应用程序中锁定单例公共(public)方法

c++ - 如何切断字符串的一部分,集合中的每个字符串都有