43 votes

Comment annuler atomiquement un std :: atomic_bool?

Le naïf négation booléenne

std::atomic_bool b;
b = !b;

ne semble pas être atomique. Je suppose que c'est parce qu' operator! déclenche une troupe de plain bool. Comment pourrait-on atomiquement effectuer l'équivalent de la négation? Le code suivant illustre le fait que la naïveté de la négation n'est pas atomique:

#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";
  }

}

Le code lance 10 threads, chacun retourne le atomic_bool un larrge, même, le nombre de fois (100000), et affiche la valeur de type boolean. Cette opération est répétée 20 fois.

EDIT: Pour ceux qui veulent exécuter ce code, je suis en utilisant GCC 4.7 instantané sur ubuntu 11.10 avec deux coeurs. Les options de compilation sont:

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

33voto

interjay Points 51000

b = !b n'est pas atomique, car il est composé à la fois d'une lecture et une écriture, dont chacun est une opération atomique.

Il y a deux options à utiliser:

  1. Au lieu de atomic<bool>, l'utilisation d'un type intégral (par exemple, atomic<int>) qui peut être 0 ou 1, et xor avec 1:

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

    Malheureusement, fetch_xor n'est pas fourni sur atomic<bool>, uniquement sur les types intégraux.

  2. Effectuer une comparaison/opération d'échange dans une boucle, jusqu'à ce qu'il réussit:

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X