8
votes

Dangers réels de plus de 2 fils écriture / lecture d'une variable

Quels sont les vrais dangers de la lecture / écriture simultanée à une seule variable?

Si j'utilise un fil pour écrire une variable et une autre pour lire la variable dans une boucle de While et il n'y a pas de danger si la variable est lue tout en étant écrite et qu'une vieille valeur est utilisée, ce qui est un danger ici?

Une lecture / écriture simultanée peut-elle causer un crash de fil ou ce qui se passe au niveau bas lorsqu'une lecture / écriture simultanée exacte se produit?


3 commentaires

"Une vieille valeur est-elle utilisée" une question assez importante aussi? Qu'est-ce que vous calculez exactement si vous ne vous souciez pas de la valeur de l'une des variables? :RÉ


Vous ne pouvez même pas de manière fiable Nombre plus, quel algorithme que vous avez à l'esprit qui peut survivre à changer de variables changent de manière imprévisible? Non, même un générateur de nombres aléatoires ne se qualifie pas.


En boucle de messagerie im en utilisant une condition de travail et une itération supplémentaire ou si la condition tandis que la condition reçoive des données indésirables, elle passerait == 0 et la boucle quittera toute façon. C'est un thread de chien de garde en boucle dans une vérification de la boucle tandis que si le fil est mort. Lorsque la fonction de thread est terminée, il définit la variable = 1 et la boucle tandis que vous quittez.


8 Réponses :


4
votes

En général, vous obtenez des résultats inattendus. Wikipedia définit deux conditions de course distinctes:

Une course critique se produit lorsque la commande dans laquelle les variables internes sont modifiées détermine l'état éventuel que la machine d'état se retrouvera dans.

Une race non critique se produit lorsque l'ordre dans lequel les variables internes sont modifiées ne modifie pas l'état éventuel. En d'autres termes, une race non critique se produit lors du passage à l'état souhaité signifie que plusieurs variables d'état internes doivent être modifiées à la fois, mais peu importe dans quel ordre ces variables d'état internes changent, l'état résultant sera le même.

La sortie ne sera donc pas toujours gâchée, cela dépend du code. C'est une bonne pratique pour TOUJOURS TRANSFERT DES CONDITIONS DE RACINGS POUR UNE ÉTAPE DU CODE PLUS ÉTAPES ET PREPENDUER DES ERREURS POSSIBLES. Rien n'est plus gênant alors de ne pas pouvoir faire confiance à vos propres données.


3 commentaires

Vous pouvez également utiliser des opérations atomiques. Un mutex peut bien être surkill.


@Deadmg: Je quitterais des opérations atomiques lorsque l'OP a interné la nécessité de Mutilex (c'est-à-dire le faire, puis le faire correctement, puis le faire rapidement, les mutiles étant bien le font bien, les opérations atomiques étant le font vite).


J'ai changé le libellé à la "affaire plus générale des conditions de course".



2
votes

Deux threads lisant la même valeur ne sont pas un problème du tout.

Le problème commence lorsqu'un thread écrit une variable non atomique et un autre fil le lit. Ensuite, les résultats de la lecture sont indéfinis. Puisqu'un fil peut être préempté (arrêté) à tout moment. Seules les opérations sur les variables atomiques sont garanties pour être non cassables. Les actions atomiques écrit généralement sur INT de type variables.

Si vous avez deux threads accédant aux mêmes données, il est la meilleure pratique + généralement inévitable d'utiliser le verrouillage (mutex, sémaphore).

htth

Mario


1 commentaires

@ Helium3: Cela dépend de la CPU. En outre, les variables doivent être alignées.



0
votes

Si la variable étant écrite et à partir de ne peut pas être mise à jour ou lecture atomique, il est possible que le lecteur ait récupéré une valeur corrompue "partiellement mise à jour".


4 commentaires

Que se passe-t-il quand par exemple un chèque comme suit alors que (IntTValue == 0) et le fil écrit la valeur INT est occupé à écrire IntTValue = 1;


@Andrei: Non. Juste un Int normal.


Ensuite, code postal :) Sans volatilité ou une barrière de mémoire, je doute que le thread de lecteur verra la sortie de fil d'écriture en raison de la mise en cache.


Eh bien, il le voit quand je l'exécuterai maintenant. Je ne veux tout simplement pas vouloir des problèmes imprévus tels qu'un accident de fil. Cette question est plus concernant le fonctionnement interne des threads et le système



0
votes
  • Vous pouvez voir une mise à jour partielle (par exemple, vous pouvez voir une variable longue avec la moitié de celle-ci provenant de la nouvelle valeur et de l'autre moitié venant de l'ancienne valeur).
  • Vous n'êtes pas garanti de voir la nouvelle valeur jusqu'à ce que vous utilisiez une barrière de mémoire ( pthread_mutex_unlock () contient une barrière de mémoire implicite).

0 commentaires

1
votes

dépend de la plate-forme. Par exemple, sur Win32, puis lus et écrire des ops de valeurs 32 bits alignés sont atomiques, c'est-à-dire que vous ne pouvez pas lire une nouvelle valeur et demi-lecture une valeur ancienne, et si vous écrivez, alors quand quelqu'un vient lire , soit ils obtiennent toute la nouvelle valeur ou la valeur ancienne. Ce n'est pas vrai pour toutes les valeurs, ou toutes les plates-formes, bien sûr.


1 commentaires

@ Hélium3: ce comportement est réellement pour x86.



1
votes

Le pire qui va arriver dépend de la mise en œuvre. Il y a tellement de mises en œuvre totalement indépendantes de Pthreads, en cours sur différents systèmes et matériels, que je doute que quiconque connaisse tout ce qui concerne tous.

si p n'est pas un pointeur à volatile puis Je pense qu'un compilateur pour une implémentation de POSIX conforme est autorisé à tourner: xxx

dans une seule vérification de * p suivi d'une boucle infinie qui ne prend pas la peine de regarder la valeur du * p du tout. En pratique, il ne sera donc pas question de savoir si vous souhaitez programmer à la norme ou au programme de comportement observé sans papiers des implémentations que vous utilisez. Ce dernier fonctionne généralement pour des cas simples, puis vous construisez sur le code jusqu'à ce que vous fassiez quelque chose de compliqué suffisant pour que cela ne fonctionne pas de manière inattendue.

dans la pratique, sur un système multi-processu qui n'a pas Caches de mémoire cohérentes, cela pourrait être très longtemps avant que la boucle voit jamais un changement de processeur différent, car sans barrières de mémoire, il ne peut jamais mettre à jour sa vue en cache de la mémoire principale. Mais Intel a des caches cohérentes, si probablement que vous ne verrez personnellement aucun retard suffisamment longtemps pour vous soucier. Si un peu pauvre essaie jamais d'exécuter votre code sur une architecture plus exotique, ils peuvent finir par avoir à le corriger.

retour à la théorie, la configuration que vous décrivez pourrait causer un crash. Imaginez une architecture hypothétique où:

  • p pointe vers un type non atomique, comme long long sur une architecture typique de 32 bits.
  • long long sur ce système comporte des représentations de piège, par exemple parce qu'il a un bit de rembourrage utilisé comme une vérification de parité.
  • L'écriture sur * p est à moitié terminée lorsque la lecture se produit
  • La demi-écriture a mis à jour certaines des bits de la valeur, mais n'a pas encore mis à jour le bit de parité.

    Bang, un comportement non défini, vous lisez une représentation piège. Il se peut que POSIX interdit certaines représentations de pièges que la norme C permet, auquel cas long long pourrait ne pas être un exemple valide pour le type de * P , mais je m'attends Vous pouvez trouver un type pour quelles représentations de pièges sont autorisées.


5 commentaires

Super. Il fonctionne actuellement sur mon Mac Intel 64 Bit, mais il sera déployé sur une planche à panda (bras double core) exécutant Ubuntu.


@ Hélium3: AH, bras à double core, qui peut ne pas avoir de cache cohérente. Je ne suis pas à jour avec le bras, mais il y a quelques années, je suis sûr qu'il y en avait à propos de la vérification.


La variable est une valeur in int. Aucun pointeur ou quoi que ce soit autre que INT.


@ Helium3, "fonctionne actuellement" ... Êtes-vous sûr de tester suffisamment pour capturer le boîtier un sur un milliard? Utilisez une instruction d'échange atomique pour cela, vous trouverez facilement du code pour cela pour différentes architectures de la CPU. Ce n'est qu'alors que vous auriez une garantie.


@ Helium3: Il peut être plus efficace d'avoir le fil de travail poster un sémaphore que le fil de surveillance attend. Ensuite, vous n'avez pas le fil de chien de garde occupé-boucle (ou même la boucle de sommeil).



0
votes

résultat n'est indéfini.

considère ce code: xxx

problème est que si vous avez N threads, le résultat peut être quelque chose entre 10 et N * 10. En effet, cela pourrait arriver que toutes les bandes de roulement se lisent la même valeur l'augmentent, puis de l'écriture de la valeur +1. Mais vous avez demandé si vous pouvez planter du programme ou du matériel.
Ça dépend. Dans la plupart des cas, des résultats erronés sont inutiles.

Pour résoudre ce problème de verrouillage, vous avez besoin de mutex ou de sémaphore.

mutex est verrouillé pour le code. En majuscule, vous verrouilleriez une partie du code en ligne xxx

où sémaphore est verrouillé pour variable xxx

basicaly Pour résoudre le même type de problème.

Vérifiez ces outils dans votre bibliothèque de roulement.

http://en.wikipedia.org/wiki/mutual_exclusion


0 commentaires

6
votes

Si deux threads accèdent une variable sans synchronisation appropriée, et au moins une de ces accès est une écriture, vous avez une course de données et un comportement non défini.

Comment les manifestations de comportement non définies sont entièrement de la mise en œuvre dépendante. Sur la plupart des architectures modernes, vous n'obtiendrez pas un piège ou une exception ou quoi que ce soit du matériel, et il sera lu quelque chose ou stocker quelque chose . La chose est que ce ne sera pas nécessairement lu ou écrit ce que vous attendiez.

E.g. Avec deux threads incrémentant une variable, vous pouvez manquer des comptes, comme décrit dans mon article à Devx: http: / /wwww.devx.com/cplus/article/42725

Pour un seul écrivain et un seul lecteur, le résultat le plus courant sera que le lecteur voit une valeur obsolète, mais vous pouvez également voir une valeur partiellement mise à jour si la mise à jour nécessite plus d'un cycle, ou la variable est divisée à travers lignes de cache. Ce qui se passe, cela dépend alors de ce que vous faites avec elle --- Si c'est un pointeur et que vous obtenez une valeur partiellement mise à jour, ce n'est peut-être pas un pointeur valide et ne pointera pas ce que vous avez entendu de toute façon, puis vous pourriez Obtenez une sorte de corruption ou d'erreur due à la déséroférance d'une valeur de pointeur non valide. Cela peut inclure le formatage de votre disque dur ou d'autres conséquences graves si la valeur de pointeur de mauvaise pointe se trouve simplement sur un enregistrement d'E / S mappé de mémoire ....


0 commentaires