94 votes

J’ai ' ve entendu i ++ isn ' t thread safe, est ++ j’ai thread-safe ?

J'ai entendu dire que i++ n'est pas thread-safe énoncé, puisque dans l'assemblée elle se réduit à la mémorisation de la valeur d'origine comme un temp quelque part, en augmentant, puis de le remplacer, ce qui pourrait être interrompu par un changement de contexte.

Cependant, je me demandais ++j'. Aussi loin que je peux dire, ce serait réduire à une seule instruction d'assemblage, tels que 'add r1, r1, 1' et puisque c'est une seule instruction, ce serait interrompu par un changement de contexte.

Quelqu'un peut-il préciser? Je suis en supposant qu'une plate-forme x86 est utilisé.

163voto

paxdiablo Points 341644

Vous avez mal entendu. Il se pourrait bien qu' "i++" est thread-safe pour un compilateur spécifique et spécifique de l'architecture du processeur, mais il n'est pas obligatoire dans les normes. En fait, depuis le multi-threading n'est pas une partie de l'ISO C ou C++ normes (a), vous ne pouvez pas envisager de quoi être thread-safe basé sur ce que vous pensez qu'il va compiler vers le bas.

C'est tout à fait possible qu' ++i pourrait compiler arbitraire séquence telle que:

load r0,[i]  ; load memory into reg 0
incr r0      ; increment reg 0
stor [i],r0  ; store reg 0 back to memory

ce qui ne serait pas "thread-safe" sur mon (imaginaire) CPU qui n'a pas de mémoire incrémenter les instructions. Ou il peut être intelligent et de les compiler en une:

lock         ; disable task switching (interrupts)
load r0,[i]  ; load memory into reg 0
incr r0      ; increment reg 0
stor [i],r0  ; store reg 0 back to memory
unlock       ; enable task switching (interrupts)

lock désactive et unlock permet d'interruptions. Mais, même alors, ce ne sont pas thread-safe dans une architecture qui a plus d'un de ces Processeurs de partage de la mémoire ( lock ne peut désactiver les interruptions pour un PROCESSEUR).

Le langage lui-même (ou bibliothèques pour elle, si elle n'est pas intégré dans la langue) fournira thread-safe constructions et vous devez l'utiliser, plutôt que de dépendre de votre compréhension (ou peut-être l'incompréhension) de ce code machine sera généré.

Des choses comme Java synchronized et pthread_mutex_lock() (C/C++ sous certains systèmes d'exploitation) sont ce que vous devez rechercher dans (un).


(a) Cette question a été posée avant le C++11 standard a été achevée. Cette itération de la norme a présenté le filetage de la prise en charge de la spécification du langage, y compris les sémaphores d'exclusion mutuelle semblables à des threads POSIX et des opérations atomiques.

43voto

Jim Mischel Points 68586

Vous ne pouvez pas faire une déclaration générale sur le ++i ou i++. Pourquoi? Envisager d'incrémentation d'un entier de 64 bits sur un système 32 bits. Sauf si le sous-jacent machine dispose d'un processeur quad mot "charge, incrémenter, stocker" de l'enseignement, l'incrémentation de la valeur qui va nécessiter plusieurs instructions, tout de qui peut être interrompue par un fil de changement de contexte.

En outre, ++i n'est pas toujours "ajouter un à la valeur." Dans un langage comme C, incrémentation d'un pointeur ajoute vraiment de la taille de l'objet pointé. C'est, si i est un pointeur vers un 32 octets de la structure, ++i ajoute de 32 octets. Alors que presque toutes les plates-formes ont une "valeur d'incrément à l'adresse mémoire" instruction atomique, tous n'ont pas atomique "ajouter de la valeur arbitraire de la valeur à l'adresse mémoire" de l'enseignement.

17voto

yogman Points 2091

Ils sont tous les deux thread-dangereux.

Un PROCESSEUR ne peut pas faire des maths directement avec la mémoire. Il n'est qu'indirectement, par le chargement de la valeur de la mémoire et de faire le calcul avec les registres du CPU.

j'++

register int a1, a2;

a1 = *(&i) ; // One cpu instruction: LOAD from memory location identified by i;
a2 = a1;
a1 += 1; 
*(&i) = a1; 
return a2; // 4 cpu instructions

++j'

register int a1;

a1 = *(&i) ; 
a1 += 1; 
*(&i) = a1; 
return a1; // 3 cpu instructions

Pour les deux cas, il y a une course à condition que les résultats de la unpreditable j'ai de la valeur.

Par exemple, supposons qu'il y a deux concurrentes ++j'ai des discussions avec chacun à l'aide de registre a1, b1, respectivement. Et, à la commutation de contexte exécutée comme suit:

register int a1, b1;

a1 = *(&i);
a1 += 1;
b1 = *(&i);
b1 += 1;
*(&i) = a1;
*(&i) = b1;

En conséquence, je ne veut pas devenir i+2, il devient i+1, ce qui est incorrect.

Pour remédier à cela, moden Processeurs, une sorte de VERROUILLER, DÉVERROUILLER instructions du processeur au cours de l'intervalle de commutation de contexte est désactivé.

Sur Win32, utilisez InterlockedIncrement() i++ pour le fil de sécurité. C'est beaucoup plus rapide que de compter sur les mutex.

11voto

Eclipse Points 27662

Si vous partagez la même un int dans les threads dans un multi-core de l'environnement, vous avez besoin d'une bonne mémoire les barrières mises en place. Cela peut signifier à l'aide de contrefil instructions (voir InterlockedIncrement dans win32 par exemple), ou à l'aide d'une langue (ou le compilateur) qui effectue un certain nombre de thread-safe garanties. Avec CPU niveau d'instruction de réapprovisionnement et de caches et d'autres questions, si vous avez ces garanties, ne présumez de rien partagées entre les threads est sûr.

Edit: Une chose que vous pouvez assumer avec la plupart des architectures, c'est que si vous traitez correctement aligné des mots simples, vous ne finirez pas avec un seul mot contenant une combinaison de deux valeurs qui ont été à la purée d'ensemble. Si les deux écritures se produire au-dessus de l'autre, on va gagner, et les autres seront ignorées. Si vous êtes prudent, vous pouvez prendre avantage de cela, et de voir que ce soit ++i ou i++ sont thread-safe dans le rédacteur unique/multiples lecteur de situation.

10voto

Max Lybbert Points 11822

Si vous voulez atomique incrément en C++, vous pouvez utiliser le C++0x bibliothèques ( std::atomic type de données) ou quelque chose comme TBB.

Il était une fois une fois que les directives de codage GNU dit mise à jour des types de données qui correspondent à un mot était "généralement sans danger", mais ce conseil est mauvais pour les machines SMP, mal pour certaines architectures, et de mal lors de l'utilisation d'un compilateur optimisant.


Afin de clarifier la "mise à jour d'un mot de type" commentaire:

Il est possible pour les deux Processeurs sur une machine SMP à écrire dans le même emplacement de mémoire dans le même cycle, et puis essayer de propager le changement pour les autres Processeurs et la mémoire cache. Même si un seul mot de données est en cours de rédaction afin que les écritures ne prenez un cycle complet, ils ont également se produisent simultanément, de sorte que vous ne pouvez pas garantir laquelle l'écriture réussit. Vous n'obtiendrez pas partiellement des données mises à jour, mais d'une écriture va disparaître parce qu'il n'y a pas d'autre moyen pour gérer ce cas.

Compare-and-swap correctement coordonnées entre plusieurs Processeurs, mais il n'y a aucune raison de croire que chaque variable affectation d'un mot de types de données utilisera compare-and-swap.

Et tout d'un compilateur optimisant n'affecte pas la façon dont un load/store est compilé, il peut changer lorsque le load/store se produit, provoquant de graves ennuis si vous vous attendez à ce que votre lit et écrit à se produire dans le même ordre qu'ils apparaissent dans le code source (le plus célèbre étant le double-vérifier le verrouillage ne fonctionne pas dans la vanille C++).

REMARQUE Ma réponse originale à cette question a également déclaré que Intel 64 bits architecture a été brisé dans le traitement avec les données 64 bits. Ce n'est pas vrai, j'ai donc édité la réponse, mais mon edit revendiquée PowerPC ont été brisées. C'est vrai lors de la lecture immédiate des valeurs (c'est à dire, des constantes) dans des registres (voir les deux sections nommées "Chargement des pointeurs" en vertu de la liste 2 et le listing 4) . Mais il y a une instruction pour le chargement de données à partir de la mémoire en un seul cycle (lmw), donc j'ai supprimé cette partie de ma réponse.

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