Quelqu'un peut-il expliquer ce qui est std::memory_order
en anglais simple et comment les utiliser avec std::atomic<>
?
J'ai trouvé la référence et quelques exemples ici, mais je ne comprends pas du tout. http://en.cppreference.com/w/cpp/atomic/memory_order
Quelqu'un peut-il expliquer ce qu'est std :: memory_order en anglais ordinaire,
La meilleure explication "en anglais simple" que j'ai trouvée pour les différents ordres de mémoire est l'article de Bartoz Milewski sur les atomes détendus: http://bartoszmilewski.com/2008/12/01/c-atomics-and-memory- commande /
Et le post de suivi: http://bartoszmilewski.com/2008/12/23/the-inscrutable-c-memory-model/
Mais notez que bien que ces articles soient une bonne introduction, ils sont antérieurs à la norme C++ 11 et ne vous diront pas tout ce que vous devez savoir pour les utiliser en toute sécurité.
et comment les utiliser avec std :: atomic <>?
Mon meilleur conseil pour vous ici est: ne le faites pas . Les atomes détendus sont (probablement) la chose la plus délicate et la plus dangereuse en C++ 11. S'en tenir à std::atomic<T>
avec l'ordre de mémoire par défaut (cohérence séquentielle) jusqu'à ce que vous soyez vraiment, vraiment sûr que vous avez un problème de performances qui peut être résolu en utilisant les ordres de mémoire détendus.
Dans le deuxième article lié ci-dessus, Bartoz Milewski arrive à la conclusion suivante:
Je ne savais pas dans quoi je m'embarquais en essayant de raisonner sur l'atome faible C++. La théorie qui les sous-tend est si complexe qu’elle est à la limite inutilisable. Il a fallu trois personnes (Anthony, Hans et moi) et une modification de la norme pour compléter la preuve d'un algorithme relativement simple. Imaginez faire la même chose pour une file d'attente sans verrou basée sur des atomes atomiques faibles!
Les valeurs std::memory_order
Vous permettent de spécifier des contraintes précises sur l'ordre de la mémoire fourni par vos opérations atomiques. Si vous modifiez et accédez à des variables atomiques à partir de plusieurs threads, transmettre les valeurs std::memory_order
À vos opérations vous permet de relax les contraintes sur le compilateur et le processeur sur l'ordre dans lequel les opérations sur ces variables atomiques deviennent visibles pour les autres threads, et les effets de synchronisation que ces opérations ont sur les données non atomiques de votre application.
L'ordre par défaut de std::memory_order_seq_cst
Est le plus contraint et fournit les propriétés "intuitives" que vous pourriez attendre: si le thread A stocke des données puis définit un drapeau atomique à l'aide de std::memory_order_seq_cst
, Alors si le thread B voit que l'indicateur est positionné, il peut voir que les données écrites par le thread A. Les autres valeurs de classement de la mémoire ne fournissent pas nécessairement cette garantie et doivent donc être utilisées avec beaucoup de prudence.
La prémisse de base est: n'utilisez rien d'autre que std::memory_order_seq_cst
(La valeur par défaut) à moins que (a) vraiment vraiment sachez ce que vous faites, et pouvez prouver que l'utilisation détendue est sûre dans tous les cas, et (b) votre profileur démontre que la structure de données et les opérations avec lesquelles vous prévoyez d'utiliser les commandes détendues sont un goulot d'étranglement.
Mon livre, C++ Concurrency in Action consacre un chapitre entier (45 pages) aux détails du modèle de mémoire C++, des opérations atomiques et des contraintes std::memory_order
, Et un autre chapitre (44 pages ) à l'utilisation d'opérations atomiques pour la synchronisation dans des structures de données sans verrouillage, et les conséquences des contraintes d'ordonnancement assouplies.
Mes entrées de blog sur algorithme de Dekker et algorithme de Peterson pour l'exclusion mutuelle démontrent certains des problèmes.
Non. Une explication "en anglais simple" prend 32 pages et peut être trouvée ici .
Si vous ne voulez pas lire cela, vous pouvez oublier l'ordre de la mémoire parce que la page à laquelle vous avez lié dit que la valeur par défaut est un ordre cohérent séquentiellement, qui est "toujours faire la bonne chose".
Pour utiliser tout autre paramètre, vous devez vraiment lire et comprendre le document ci-dessus et les exemples qu'il contient.
En bref, votre compilateur et votre processeur peuvent exécuter des instructions dans un ordre différent de la façon dont vous les avez écrites. Pour un seul thread, ce n'est pas un problème car il apparaîtra correct. Pour plusieurs threads sur plusieurs processeurs, cela devient un problème. L'ordre de la mémoire en C++ limite ce que votre compilateur/CPU peut faire et résout ces problèmes.
Par exemple, si vous regardez mon article sur verrouillage à double vérification vous pouvez voir comment la commande s'embrouille avec ce modèle - il mentionne que l'ordre de la mémoire atomique peut être utilisé pour le corriger.
À propos de la réorganisation elle-même, vous pouvez également envisager CPU Reordering - encore une fois, le compilateur peut également effectuer des réorganisations.
Sachez que tous les documents sur ce sujet (y compris le mien) proposent des scénarios théoriques. Les processeurs les plus courants, comme x86, ont des garanties de commande très solides, de sorte qu'un grand nombre de commandes explicites n'est tout simplement pas nécessaire. Ainsi, même si vous n'utilisez pas les atomiques C++ 11 appropriés, votre code fonctionnera probablement.
Comme l'a mentionné zvrba, le sujet est en fait assez détaillé. La documentation du noyau Linux sur barrières mémoire contient également de nombreuses informations détaillées.
Il y a un peu d'anglais dans le wiki de GCC. ;)