L'écriture d'un (filature) fil de barrière à l'aide de c++11 atomics

Je suis en train de me familiariser avec le c++11 atomics, j'ai donc essayé d'écrire une barrière de classe pour les threads (avant que quelqu'un se plaint de ne pas utiliser de classes existantes: c'est plus pour l'apprentissage/l'amélioration de soi qu'à aucun besoin réel). ma classe ressemble fondamentalement comme suit:

class barrier
{
private:
    std::atomic<int> counter[2];
    std::atomic<int> lock[2];
    std::atomic<int> cur_idx;
    int thread_count;
public:
    //constructors...
    bool wait();
};

Tous les membres sont initialisées à zéro, à l'exception de thread_count, qui détient la plus appropriée de les compter.
J'ai implémenté la fonction attendre que

int idx  = cur_idx.load();
if(lock[idx].load() == 0)
{
    lock[idx].store(1);
}
int val = counter[idx].fetch_add(1);
if(val >= thread_count - 1)
{
    counter[idx].store(0);
    cur_idx.fetch_xor(1);
    lock[idx].store(0);
    return true;
}
while(lock[idx].load() == 1);
return false;

Cependant, quand on essaie de l'utiliser avec les deux fils (thread_count est 2) blé premier thread est dans la boucle d'attente, mais le deuxième thread n'a pas de déverrouiller la barrière (il semble qu'il n'a même pas eu à int val = counter[idx].fetch_add(1);, mais je ne suis pas trop sûr de vous. Cependant, quand je suis en utilisant gcc atomique intrinsèques en utilisant volatile int au lieu de std::atomic<int> et de l'écriture wait comme suit:

int idx = cur_idx;
if(lock[idx] == 0)
{
    __sync_val_compare_and_swap(&lock[idx], 0, 1);
}
int val = __sync_fetch_and_add(&counter[idx], 1);
if(val >= thread_count - 1)
{
    __sync_synchronize();
    counter[idx] = 0;
    cur_idx ^= 1;
    __sync_synchronize();
    lock[idx] = 0;
    __sync_synchronize();
    return true;
}
while(lock[idx] == 1);
return false;

il fonctionne très bien. De ma compréhension, il ne devrait pas y avoir de différences fondamentales entre les deux versions (plus au point si quelque chose que la seconde devrait être moins susceptibles de travailler). Alors, laquelle des situations suivantes s'applique?

  1. J'ai eu de la chance avec le deuxième de la mise en œuvre et de mon algorithme est de la merde
  2. Je n'ai pas bien compris std::atomic et il y a un problème avec la première variante (mais pas le second)
  3. Il devrait fonctionner, mais la mise en œuvre expérimentale de c++11 bibliothèques n'est pas aussi mature que j'ai espéré

Pour le dossier je suis en utilisant 32 bits avec mingw gcc 4.6.1

Le code d'appel qui ressemble à ceci:

spin_barrier b(2);
std::thread t([&b]()->void
{
    std::this_thread::sleep_for(std::chrono::duration<double>(0.1));
    b.wait();
});
b.wait();
t.join();

Depuis mingw n'est pas whave <thread> les en-têtes de jet-je utiliser un auto version écrite de ce qui, fondamentalement, enveloppe le pthread approprié fonctions (avant que quelqu'un pose la question: oui, il fonctionne sans la barrière, de sorte qu'il ne devrait pas être un problème avec l'emballage)
Toutes les suggestions seraient appréciées.

edit: Explication de l'algorithme pour le rendre plus clair:

  • thread_count est le nombre de threads qui doit attendre la barrière (donc si thread_count fils sont dans la barrière de tous pouvez laisser la barrière).
  • lock est mis à un lorsque le premier (ou du tout) fil entre la barrière.
  • counter compte le nombre de threads à l'intérieur de la barrière et est automatiquement incrémenté pour chaque thread
  • if counter>=thread_count tous les fils sont à l'intérieur de la barrière afin de compteur et de verrouillage sont remis à zéro
  • sinon, le thread attend le lock pour devenir zéro
  • dans la prochaine utilisation de la barrière de différentes variables (counter, lock) sont utilisés assurer il n'y a pas de problèmes si les threads sont toujours en attente de la première utilisation de la barrière (par exemple, ils avaient été mis en pause lorsque la barrière est levée)

edit2:
J'ai maintenant testé à l'aide de gcc 4.5.1 sous linux, où les deux versions semblent très bien fonctionner, ce qui semble signaler un problème avec mingw est std::atomic, mais je ne suis pas encore complètement convaincu, depuis la recherche dans le <atomic> en-tête revaled que la plupart des fonctions simplement appeler le gcc-atomique, ce qui signifie qu'il ne devrait pas bea différence entre les deux versions

Je n'ai pas joué avec le c++11 atomics encore, mais je suis vraiment surpris de vous voir à l'aide de GCC instrinsics (par exemple __sync_fetch_and_add). Je dirais, ceux-ci devraient être inutiles avec c++11?
J'ai mis en place la version à l'aide de GCC intrinsèques lors de l'une à l'aide de atomics n'ai pas de travail à des fins de comparaison (aka si les deux n'a pas fonctionné, j'aurais cru qu'il y est quelque chose de vraiment mal avec mon algorithme)
N'est-il pas une condition de concurrence dans le if load then store? Ne devrait-ce pas être un if (x.exchange(1)) ou quelque chose comme ça?
SB: non, pas vraiment car le si n'est même pas nécessaire, il existe, store(1) sans if devrait fonctionner tout aussi bien, depuis lock est mis à 1 et, ensemble de retour après tous les fils dans la barrière a passé cette. Le if(lock.load()) n'est là que pour éviter unnecerry accès en écriture à la cacheline (maintenant que j'y pense j'ai pu commencer à définir la prochaine écluse à 1 dans le if(val >= thread_count - 1) partie, pour obtenir le même effet
Je pense que vous avez raison, je n'étais pas le lire attentivement assez.

OriginalL'auteur Grizzly | 2011-11-13