c++ - 如何以原子方式否定 std::atomic_bool?

标签 c++ multithreading c++11 atomic

朴素的 bool 否定

std::atomic_bool b;
b = !b;

似乎不是原子的。我怀疑这是因为 operator! 触发了转换为纯 bool。一个人将如何原子地执行等效的否定?以下代码说明了天真的否定不是原子的:

#include <thread>
#include <vector>
#include <atomic>
#include <iostream>

typedef std::atomic_bool Bool;

void flipAHundredThousandTimes(Bool& foo) {
  for (size_t i = 0; i < 100000; ++i) {
    foo = !foo;
  }
}

// Launch nThreads std::threads. Each thread calls flipAHundredThousandTimes 
// on the same boolean
void launchThreads(Bool& foo, size_t nThreads) {

  std::vector<std::thread> threads;
  for (size_t i = 0; i < nThreads; ++i) {
    threads.emplace_back(flipAHundredThousandTimes, std::ref(foo));
  }

  for (auto& thread : threads) thread.join();

}

int main() {

  std::cout << std::boolalpha;
  Bool foo{true};

  // launch and join 10 threads, 20 times.
  for (int i = 0; i < 20; ++i) {
    launchThreads(foo, 10);
    std::cout << "Result (should be true): " << foo << "\n";
  }

}

代码启动了 10 个线程,每个线程将 atomic_bool 翻转一个大的偶数次 (100000),并打印出 bool 值。这重复 20 次。

编辑:对于那些想要运行此代码的人,我在具有两个内核的 ubuntu 11.10 上使用 GCC 4.7 快照。编译选项有:

-std=c++0x -Wall -pedantic-errors -pthread

最佳答案

b = !b不是原子的,因为在 C++ 源代码中,您有一个原子纯读取 b (等价于 b.load() ,然后对 b 进行单独的原子分配(等价于 b.store() )。

在 C++ 抽象机中没有任何东西可以将整个组合变成原子 RMW 操作,并且没有将任意操作组合成原子 RMW 操作的语法(除了将其放入 CAS 重试循环中)。


有两种选择:

  1. 而不是 atomic<bool> , 使用整数类型(例如 atomic<int>atomic<unsigned char> ),可以是 0 或 1,并与 1 进行异或:

    std::atomic<int> flag(0);
    
    flag ^= 1;        //equivalent to flag.fetch_xor(1);
    

    不幸的是,fetch_xor未在 atomic<bool> 上提供, 仅适用于整数类型。

  2. 循环执行比较/交换操作,直到成功:

    std::atomic<bool> flag(false);
    
    bool oldValue = flag.load();
    while (!flag.compare_exchange_weak(oldValue, !oldValue)) {}
    

    不幸的是,x86 的编译器通常不会将此循环优化为
    lock xor byte [flag], 1在 asm 中;你会得到一个实际的 cmpxchg 重试循环。在实践中 cmpxchg 重试循环在低争用情况下很好。在最坏的情况下,这不是无等待的,而是无锁的,因为每次重试时至少有一个线程会取得进展。 (实际上,对于哪个内核甚至可以访问缓存行进行尝试的硬件仲裁会更加复杂。)

    如果可能存在高争用,请选择允许您使用原子异或的整数版本。

关于c++ - 如何以原子方式否定 std::atomic_bool?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9806200/

相关文章:

c++ - 专业游戏开发环境中的最佳 C++ 编程实践?

尝试访问对象的方法时出现 C++ Qt 段错误

java - 以最快的方式复制大文件

android - 下载安卓大文件

c++ - 应该使用 std::stof 和 atof 之间的区别是什么?

c++ - 无法输出到 eat、out 或 text,但可以将数据附加到输入文件,

c++ - 省略号 try catch on c++

java - 如何将非并行流与并行流(一个生产者多个消费者)互连

c++ - std::array 的内联初始化有什么问题?

c++ - 在 C++11 中定义 lambda 函数不会在类内部编译