如何自动否定std :: atomic_bool?

jua*_*nza 40 c++ multithreading atomic c++11

天真的布尔否定

std::atomic_bool b;
b = !b;
Run Code Online (Sandbox Code Playgroud)

似乎不是原子的.我怀疑这是因为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";
  }

}
Run Code Online (Sandbox Code Playgroud)

代码启动了10个线程,每个线程都会将atomic_bool翻转为多次,甚至是次数(100000),并打印出布尔值.这重复20次.

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

-std=c++0x -Wall -pedantic-errors -pthread
Run Code Online (Sandbox Code Playgroud)

int*_*jay 29

b = !b 不是原子的,因为它由read和write组成,每个都是原子操作.

有两种选择:

  1. 而不是b使用整数类型(例如b.load()),它可以是0或1,而xor与1:

    std::atomic<int> flag(0);
    
    flag ^= 1;        //equivalent to flag.fetch_xor(1);
    
    Run Code Online (Sandbox Code Playgroud)

    不幸的是,b没有提供b.store(),只提供整体类型.

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

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

  • @zch:我觉得不行.如果另一个线程成功修改了变量,它只需要再次循环.所以有些工作总是在至少一个线程上完成 - 换句话说,这是无锁的,但不是等待的. (5认同)
  • @einpoklum没有谬论。“ x ^ = 1”是单个原子操作,而“ x =!x”(还有x = x ^ 1`)是两个原子操作。 (2认同)
  • @BruceDawson `std::atomic` 变量的全部要点是对它们的操作,包括 `^=`,被定义为原子的。 (2认同)
  • @BruceDawson 实际上,当您按照这些操作的链接进行操作时会更清楚:https://en.cppreference.com/w/cpp/atomic/atomic/operator_arith2 该操作是读-修改-写操作。请注意,您在任何地方都无法保证硬件加速、无锁、原子性。所以这些RMW操作可以用互斥锁来实现。 (2认同)