9
votes

Dois-je utiliser le verrouillage avec des entiers dans des threads C ++

Si j'accède à un seul type entier (E.G. Long, Int, Bool, etc.) Dans plusieurs threads, dois-je utiliser un mécanisme de synchronisation tel qu'un mutex pour les verrouiller. Je crois comprendre que comme des types atomiques, je n'ai pas besoin de verrouiller l'accès à un seul fil, mais je vois beaucoup de code là-bas qui utilise un verrouillage. Profilage Ce code montre qu'il existe une performance significative pour utiliser des serrures, donc je préférerais pas. Donc, si l'article que j'accède correspond à une largeur de bus entier (par exemple 4 octets sur un processeur 32 bits) Dois-je verrouiller l'accès à celui-ci lorsqu'il est utilisé sur plusieurs threads? Mettre un autre moyen, si le fil a écrit en integer variable x en même temps que le thread B lisez à partir de la même variable, est-il possible que le fil b pourrait finir de quelques octets de la valeur précédente mélangée à quelques octets de la valeur étant écrite? Cette architecture est-elle dépendante, par exemple OK pour les entiers d'octets de 4 octets sur les systèmes 32 bits mais dangereux sur 8 octets dans des systèmes 64 bits?

EDIT: vient de voir cette Poste associé qui aide un peu juste.


0 commentaires

7 Réponses :


4
votes

Il n'y a pas de support pour les variables atomiques en C ++, de sorte que vous avez besoin de verrouillage. Sans verrouillage, vous ne pouvez spéculer que quelles instructions exactes seront utilisées pour la manipulation des données et si ces instructions garantiraient un accès atomique - ce n'est pas la manière dont vous développez des logiciels fiables.


1 commentaires

Je dois être en désaccord (un peu). Il n'y a pas de variables atomiques en C ++, non, mais il n'y a pas de serrure non plus. Dès que vous entrez dans la multiplication multipliée, vous devez compter sur les garanties données par votre compilateur spécifique. Et que a beaucoup de garanties d'atomicité. Généralement, les accès aux objets de taille de mots seront atomiques. Bien sûr, la langue C ++ ne garantit pas cela, mais votre compilateur spécifique fait probablement probablement. Bien entendu, le compilateur rend généralement peu de garanties sur la réorganisation, de sorte que vous aurez peut-être encore besoin de verrouillage, ou d'au moins des barrières de mémoire, en fonction de ce que vous faites exactement.



3
votes

Oui, il serait préférable d'utiliser la synchronisation. Toute données accessible par plusieurs threads doit être synchronisée.

Si c'est la plate-forme Windows, vous pouvez également vérifier ici: Accès variable interlocké .


0 commentaires

2
votes

Oui. Si vous êtes sur Windows, vous pouvez consulter verrouillé < / a> fonctions / variables et si vous êtes de la persuasion de boost, vous pouvez regarder leur implémentation de variables atomiques .

Si Boost est trop lourd, placez-le " Atomic C ++ " dans votre moteur de recherche préféré vous donnera beaucoup de nourriture pour la pensée.


0 commentaires

9
votes

Vous ne verrouillez jamais de valeur - vous verrouillez une opération sur une valeur.

C & C ++ Ne mentionnez pas explicitement les threads ou les opérations atomiques - de sorte que les opérations qui ressemblent à elles pourraient ou doivent être atomiques - ne sont pas garanties par la spécification de langue d'être atomique.

Ce serait certes un compilateur assez déviant qui a géré une lecture non atomique sur un INT: Si vous avez une opération qui lit une valeur - theres probablement pas besoin de le protéger. Cependant, il pourrait être non atomique s'il couvre une limite de mot machine.

opérations aussi simples que m_counter ++ implique une extraction, une incrément et une opération de stockage - une condition de course: un autre thread peut modifier la valeur après la fetch mais avant le magasin - et donc doit être protégé par Un mutex - ou trouver votre prise en charge de vos compilateurs pour des opérations imbriquées. MSVC a des fonctions telles que _InterlockedinCrement () qui incremportera en toute sécurité un emplacement de mémoire tant que toutes les autres écrivies utilisent de la même manière à la mise à jour de l'emplacement de la mémoire - qui est des commandes de grandeur plus légères que d'invoquer une section même critique.

GCC a des fonctions intrinsèques telles que __ sync_add_and_fetch qui peut également être utilisée pour effectuer des opérations verrouillées sur des valeurs Word de la machine.


1 commentaires

Merci pour cela, Interlockedexchange est probablement la fonction que je recherche, car un seul fil écrit réellement à la variable en question, alors que d'autres le lisent simplement.



4
votes

Dans 99,99% des cas, vous DOIT LOCK, même s'il est accédé à des variables apparemment atomiques. Comme le compilateur C ++ n'est pas au courant de la multi-filetage sur le niveau de langue, il peut faire beaucoup de réorganisations non triviales.

cas au point: j'ai été mordu par une implémentation de verrouillage de spin où déverrouillez simplement l'attribution de zéro à un Volatile Variable entier. Le compilateur était la réorganisation des opérations de déverrouillage avant l'opération réelle sous la serrure, sans surprise, conduisant à des accidents mystérieux.

Voir:

  1. Code LOCK-Free: Un faux sens de la sécurité
  2. Les threads ne peuvent pas être implémentés comme une bibliothèque

2 commentaires

Bug compilateur. La volatile est une limite d'optimisation.


@Joshua, je sens du sarcasme? Sinon, alors non, ce n'est pas un bug de compilateur. Rien n'empêche le compilateur de réorganiser la réorganisation de rouge / écrit non volatile autour des volatiles, il ne peut que réorganiser la réorganisation des lectures volatiles / écrit entre eux. Voir cette question: Stackoverflow.com/questions/2535148/...



5
votes

Si vous êtes sur une machine avec plus d'un noyau, vous besoin faire les choses correctement même si les écrits d'un entier sont atomiques. Les problèmes sont deux fois:

  1. Vous devez arrêter le compilateur d'optimiser l'écriture réelle! (Un peu important cela.; -))
  2. Vous avez besoin de barrières de mémoire (pas de choses modélisées en C) pour vous assurer que les autres noyaux prennent connaissance du fait que vous avez changé de choses. Sinon, vous serez emmêlé dans des caches entre tous les processeurs et d'autres détails sales comme celui-là.

    Si c'était juste la première chose, vous alliez bien de marquer la variable volatile , mais le second est vraiment le tueur et vous ne vraiment voir le différence sur une machine multicœur. Ce qui se trouve être une architecture qui devient beaucoup plus courante qu'elle était ... oups! Il est temps d'arrêter d'être bâclé; Utilisez le code correct mutex (ou synchronisation ou autre) pour votre plate-forme et tous les détails de la façon de faire de la mémoire de travail comme vous le croyez.


0 commentaires

2
votes

La multithreading est dure et complexe. Le nombre de problèmes difficiles à diagnostiquer les problèmes qui peuvent contourner est assez grand. En particulier, sur les architectures Intel lit et écrit à partir d'entiers alignés 32 bits est garanti pour être atomique dans le processeur, mais cela ne signifie pas qu'il est prudent de le faire dans des environnements multithreads.

Sans gardes corrects, le compilateur et / ou le processeur peuvent réorganiser les instructions de votre bloc de code. Il peut cacher des variables dans des registres et ils ne seront pas visibles dans d'autres threads ...

Le verrouillage est coûteux, et il existe différentes implémentations de structures de données sans verrouillage pour optimiser les performances élevées, mais il est difficile de le faire correctement. Et le problème est que les bugs de la concurrence sont généralement obscurs et difficiles à déboguer.


0 commentaires