web-dev-qa-db-ja.com

std :: 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)反転させ、ブール値を出力します。これを20回繰り返します。

[〜#〜] edit [〜#〜]:このコードを実行したい人のために、2つのコアを備えたubuntu11.10でGCC4.7スナップショットを使用しています。コンパイルオプションは次のとおりです。

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

_b = !b_はアトミックではありません。これは、C++ソースでbb.load()と同等)のアトミックな純粋な読み取りがあり、bへの個別のアトミックな割り当てがあるためです。 (b.store()と同等)。

組み合わせ全体をC++抽象マシンのアトミックRMW操作にするものはなく、任意の操作をアトミックRMW操作に構成するための構文もありません(CAS再試行ループに入れる以外)。


使用するオプションは2つあります。

  1. _atomic<bool>_の代わりに、0または1の整数型(例:_atomic<int>_または_atomic<unsigned char>_)を使用し、1でxorします。

    _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の再試行ループは、競合が少なくても問題ありません。最悪の場合、これは待機なしではありませんが、すべてが再試行するたびに少なくとも1つのスレッドが進行するため、ロックなしです。 (実際には、コアがキャッシュラインにアクセスして試行するハードウェアアービトレーションではより複雑になります。)

    高い競合が発生する可能性がある場合は、アトミックxorを使用できる整数バージョンを選択してください。

31
interjay