Cela signifie-t-il que deux threads ne peuvent pas modifier simultanément les données sous-jacentes? Ou cela signifie-t-il que le segment de code donné fonctionnera avec des résultats prévisibles lorsque plusieurs threads exécutent ce segment de code?
multithreading
language-agnostic
programming-languages
concurrency
Varun Mahajan
la source
la source
Réponses:
De Wikipédia:
Lire la suite:
http://en.wikipedia.org/wiki/Thread_safety
en allemand: http://de.wikipedia.org/wiki/Threadsicherheit
en français: http://fr.wikipedia.org/wiki/Threadsafe (très court)
la source
Le code thread-safe est un code qui fonctionnera même si de nombreux threads l'exécutent simultanément.
http://mindprod.com/jgloss/threadsafe.html
la source
Une question plus informative est ce qui rend le code non sûr pour les threads - et la réponse est qu'il y a quatre conditions qui doivent être vraies ... Imaginez le code suivant (et sa traduction en langage machine)
la source
J'aime la définition de la concurrence Java de Brian Goetz dans la pratique pour son exhaustivité
"Une classe est thread-safe si elle se comporte correctement lorsqu'elle est accessible à partir de plusieurs threads, indépendamment de la planification ou de l'entrelacement de l'exécution de ces threads par l'environnement d'exécution, et sans synchronisation ou autre coordination supplémentaire de la part du code appelant. "
la source
Comme d'autres l'ont souligné, la sécurité des threads signifie qu'un morceau de code fonctionnera sans erreur s'il est utilisé par plusieurs threads à la fois.
Il faut savoir que cela a parfois un coût, du temps informatique et un codage plus complexe, donc ce n'est pas toujours souhaitable. Si une classe peut être utilisée en toute sécurité sur un seul thread, il peut être préférable de le faire.
Par exemple, Java a deux classes presque équivalentes,
StringBuffer
etStringBuilder
. La différence est qu'elleStringBuffer
est adaptée aux threads, donc une seule instance de aStringBuffer
peut être utilisée par plusieurs threads à la fois.StringBuilder
n'est pas thread-safe et est conçu comme un remplacement plus performant pour ces cas (la grande majorité) lorsque la chaîne est construite par un seul thread.la source
Le code thread-safe fonctionne comme spécifié, même lorsqu'il est entré simultanément par différents threads. Cela signifie souvent que les structures de données internes ou les opérations qui doivent s'exécuter sans interruption sont protégées contre différentes modifications en même temps.
la source
Un moyen plus simple de le comprendre est ce qui rend le code non thread-safe. Il y a deux problèmes principaux qui feront qu'une application filetée aura un comportement indésirable.
Accès à la variable partagée sans verrouillage
Cette variable peut être modifiée par un autre thread lors de l'exécution de la fonction. Vous voulez l'empêcher avec un mécanisme de verrouillage pour être sûr du comportement de votre fonction. La règle générale est de garder le verrou le plus court possible.
Blocage provoqué par la dépendance mutuelle sur la variable partagée
Si vous avez deux variables partagées A et B. Dans une fonction, vous verrouillez d'abord A puis plus tard vous verrouillez B. Dans une autre fonction, vous commencez à verrouiller B et après un certain temps, vous verrouillez A. Cette est une impasse potentielle où la première fonction attendra que B soit déverrouillée lorsque la deuxième fonction attendra que A soit déverrouillé. Ce problème ne se produira probablement pas dans votre environnement de développement et uniquement de temps en temps. Pour l'éviter, toutes les serrures doivent toujours être dans le même ordre.
la source
Oui et non.
La sécurité des threads est un peu plus que la simple vérification que vos données partagées ne sont accessibles que par un seul thread à la fois. Vous devez vous assurer un accès séquentiel aux données partagées, tout en évitant les conditions de course , blocages , cycles non , et la famine des ressources .
Des résultats imprévisibles lorsque plusieurs threads sont en cours d'exécution ne sont pas une condition requise du code thread-safe, mais il s'agit souvent d'un sous-produit. Par exemple, vous pouvez avoir un schéma producteur-consommateur configuré avec une file d'attente partagée, un thread producteur et quelques threads consommateur, et le flux de données peut être parfaitement prévisible. Si vous commencez à introduire plus de consommateurs, vous verrez des résultats plus aléatoires.
la source
En substance, de nombreuses choses peuvent mal tourner dans un environnement multi-thread (réordonnancement des instructions, objets partiellement construits, même variable ayant des valeurs différentes dans différents threads en raison de la mise en cache au niveau du processeur, etc.).
J'aime la définition donnée par Java Concurrency en pratique :
Par correctement, ils signifient que le programme se comporte conformément à ses spécifications.
Exemple artificiel
Imaginez que vous implémentez un compteur. On pourrait dire qu'il se comporte correctement si:
counter.next()
ne renvoie jamais une valeur qui a déjà été retournée auparavant (nous supposons qu'il n'y a pas de débordement, etc. pour plus de simplicité)Un compteur de threads sûrs se comporterait selon ces règles quel que soit le nombre de threads qui y accèdent simultanément (ce qui ne serait généralement pas le cas d'une implémentation naïve).
Remarque: cross-post sur les programmeurs
la source
Simplement - le code fonctionnera correctement si plusieurs threads exécutent ce code en même temps.
la source
Ne confondez pas la sécurité des fils avec le déterminisme. Le code thread-safe peut également être non déterministe. Étant donné la difficulté de déboguer les problèmes avec le code threadé, c'est probablement le cas normal. :-)
La sécurité des threads garantit simplement que lorsqu'un thread modifie ou lit des données partagées, aucun autre thread ne peut y accéder d'une manière qui modifie les données. Si votre code dépend d'un certain ordre d'exécution pour être correct, vous avez besoin d'autres mécanismes de synchronisation au-delà de ceux requis pour la sécurité des threads pour garantir cela.
la source
Je voudrais ajouter plus d'informations en plus d'autres bonnes réponses.
La sécurité des threads implique que plusieurs threads peuvent écrire / lire des données dans le même objet sans erreurs d'incohérence de mémoire. Dans un programme hautement multi-thread, un programme thread-safe ne provoque pas d'effets secondaires sur les données partagées .
Jetez un œil à cette question SE pour plus de détails:
Que signifie threadsafe?
Le programme Thread Safe garantit la cohérence de la mémoire .
Depuis la page de documentation d'Oracle sur l'API simultanée avancée:
Propriétés de cohérence de la mémoire:
Le chapitre 17 de la spécification du langage Java ™ définit la relation passe-avant sur les opérations de mémoire telles que les lectures et les écritures de variables partagées. Les résultats d'une écriture par un thread sont garantis visibles à la lecture par un autre thread uniquement si l'opération d'écriture se produit avant l'opération de lecture .
Les constructions
synchronized
etvolatile
, ainsi que les méthodesThread.start()
etThread.join()
, peuvent former des relations qui se produisent avant .Les méthodes de toutes les classes dans
java.util.concurrent
et ses sous-packages étendent ces garanties à une synchronisation de niveau supérieur. En particulier:Runnable
à unExecutor
événement, avant le début de son exécution. De même pour Callables soumis à unExecutorService
.Future
actions se produisant avant la suite de la récupération du résultat viaFuture.get()
dans un autre thread.Lock.unlock, Semaphore.release, and CountDownLatch.countDown
actions se produisant avant, après une méthode "d'acquisition" réussie, commeLock.lock, Semaphore.acquire, Condition.await, and CountDownLatch.await
sur le même objet du synchroniseur dans un autre thread.Exchanger
, les actions antérieures àexchange()
dans chaque thread se produisent avant celles qui suivent l'échange correspondant () dans un autre thread.CyclicBarrier.await
etPhaser.awaitAdvance
(ainsi que ses variantes) se produisent avant les actions effectuées par l'action barrière et les actions effectuées par l'action barrière se produisent avant les actions après un retour réussi de l'attente correspondante dans d'autres threads.la source
Pour compléter d'autres réponses:
La synchronisation n'est un problème que lorsque le code de votre méthode fait l'une des deux choses suivantes:
Cela signifie que les variables définies DANS VOTRE méthode sont toujours threadsafe. Chaque appel à une méthode a sa propre version de ces variables. Si la méthode est appelée par un autre thread, ou par le même thread, ou même si la méthode s'appelle elle-même (récursivité), les valeurs de ces variables ne sont pas partagées.
La programmation des threads n'est pas garantie d'être circulaire . Une tâche peut monopoliser totalement le CPU au détriment de threads de même priorité. Vous pouvez utiliser Thread.yield () pour avoir une conscience. Vous pouvez utiliser (en java) Thread.setPriority (Thread.NORM_PRIORITY-1) pour réduire la priorité d'un thread
Méfiez-vous de:
la source
Oui et oui. Cela implique que les données ne sont pas modifiées par plus d'un thread simultanément. Cependant, votre programme peut fonctionner comme prévu et apparaître thread-safe, même s'il ne l'est pas fondamentalement.
Notez que l'imprévisibilité des résultats est une conséquence des «conditions de course» qui entraînent probablement une modification des données dans un ordre autre que celui attendu.
la source
Répondons à cela par l'exemple:
La
countTo10
méthode en ajoute un au compteur et renvoie ensuite true si le nombre a atteint 10. Il ne doit renvoyer true qu'une seule fois.Cela fonctionnera tant qu'un seul thread exécutera le code. Si deux threads exécutent le code en même temps, divers problèmes peuvent survenir.
Par exemple, si le décompte commence par 9, un thread peut ajouter 1 au décompte (ce qui en fait 10), puis un deuxième thread peut entrer dans la méthode et ajouter à nouveau 1 (ce qui en fait 11) avant que le premier thread ait la possibilité d'exécuter la comparaison avec 10 Ensuite, les deux threads effectuent la comparaison et constatent que le nombre est 11 et qu'aucun ne renvoie true.
Ce code n'est donc pas sûr pour les threads.
En substance, tous les problèmes de multithread sont causés par une certaine variation de ce type de problème.
La solution est de s'assurer que l'addition et la comparaison ne peuvent pas être séparées (par exemple en entourant les deux instructions par une sorte de code de synchronisation) ou en concevant une solution qui ne nécessite pas deux opérations. Un tel code serait thread-safe.
la source
Au moins en C ++, je pense que le thread-safe est un peu impropre en ce sens qu'il laisse beaucoup de côté du nom. Pour être thread-safe, le code doit généralement être proactif à son sujet. Ce n'est généralement pas une qualité passive.
Pour qu'une classe soit sécurisée, elle doit avoir des fonctionnalités "supplémentaires" qui ajoutent une surcharge. Ces fonctionnalités font partie de l'implémentation de la classe et sont généralement cachées de l'interface. C'est-à-dire que différents threads peuvent accéder à n'importe quel membre de la classe sans jamais avoir à se soucier des conflits avec un accès simultané par un thread différent ET peuvent le faire de manière très paresseuse, en utilisant un ancien style de codage humain ordinaire, sans avoir à le faire tout ce truc de synchronisation fou qui est déjà roulé dans le ventre du code appelé.
Et c'est pourquoi certaines personnes préfèrent utiliser le terme synchronisé en interne .
Ensembles de terminologie
Il y a trois ensembles principaux de terminologie pour ces idées que j'ai rencontrées. Le premier et historiquement le plus populaire (mais le pire) est:
Le second (et mieux) est:
Un troisième est:
Analogies
thread safe ~ thread proof ~ synchronisé en interne
Un exemple de système synchronisé en interne (aka. Thread-safe ou thread proof ) est un restaurant où un hôte vous accueille à la porte et vous empêche de faire la queue vous-même. L'hôte fait partie du mécanisme du restaurant pour traiter avec plusieurs clients et peut utiliser des astuces plutôt délicates pour optimiser l'assise des clients en attente, comme la prise en compte de la taille de leur fête ou le temps dont ils ont l'air. , ou même prendre des réservations par téléphone. Le restaurant est synchronisé en interne car tout cela fait partie de l'interface pour interagir avec lui.
pas compatible avec les threads (mais agréable) ~ compatible avec les threads ~ synchronisé en externe ~ libre
Supposons que vous alliez à la banque. Il y a une ligne, c'est-à-dire un conflit pour les caissiers. Parce que vous n'êtes pas un sauvage, vous reconnaissez que la meilleure chose à faire au milieu d'une dispute pour une ressource est de faire la queue comme un être civilisé. Personne ne vous oblige techniquement à le faire. Nous espérons que vous avez la programmation sociale nécessaire pour le faire vous-même. En ce sens, le lobby bancaire est synchronisé en externe. Faut-il dire que c'est thread-dangereux? c'est ce que cela implique si vous optez pour l' ensemble de terminologie bipolaire thread-safe et thread-unsafe . Ce n'est pas un très bon ensemble de termes. La meilleure terminologie est synchronisée en externe,Le lobby bancaire n'est pas hostile à l'accès de plusieurs clients, mais il ne fait pas non plus le travail de synchronisation. Les clients le font eux-mêmes.
Ceci est également appelé "filetage libre", où "libre" est comme dans "sans poux" - ou dans ce cas, les verrous. Eh bien, plus précisément, les primitives de synchronisation. Cela ne signifie pas que le code peut s'exécuter sur plusieurs threads sans ces primitives. Cela signifie simplement qu'il ne vient pas avec eux déjà installés et c'est à vous, l'utilisateur du code, de les installer vous-même comme bon vous semble. L'installation de vos propres primitives de synchronisation peut être difficile et nécessite une réflexion approfondie sur le code, mais peut également conduire au programme le plus rapide possible en vous permettant de personnaliser la façon dont le programme s'exécute sur les CPU hyperthreadés d'aujourd'hui.
pas sûr pour les threads (et mauvais) ~ hostile aux threads ~ non synchronisable
Un exemple d'analogie quotidienne avec un système hostile aux fils est celui d'un imbécile avec une voiture de sport refusant d'utiliser ses oeillères et de changer de voie à volonté. Leur style de conduite est hostile au fil ou non psychronisable car vous n'avez aucun moyen de vous coordonner avec eux, ce qui peut entraîner des conflits pour la même voie, sans résolution, et donc un accident car deux voitures tentent d'occuper le même espace, sans aucun protocole éviter cela. Ce modèle peut également être considéré plus largement comme antisocial, ce que je préfère car il est moins spécifique aux threads et donc plus généralement applicable à de nombreux domaines de la programmation.
Pourquoi thread safe et al. sont une mauvaise terminologie
Le premier et le plus ancien ensemble terminologique ne parviennent pas à faire la distinction plus fine entre l' hostilité des threads et la compatibilité des threads . La compatibilité des threads est plus passive que la soi-disant sécurité des threads, mais cela ne signifie pas que le code appelé n'est pas sûr pour une utilisation simultanée des threads. Cela signifie simplement qu'il est passif à propos de la synchronisation qui permettrait cela, le reportant au code appelant, au lieu de le fournir dans le cadre de son implémentation interne. La compatibilité avec les threads est la façon dont le code devrait probablement être écrit par défaut dans la plupart des cas, mais cela est malheureusement souvent souvent considéré à tort comme un thread non sécurisé, comme s'il était intrinsèquement anti-sécurité, ce qui est un point de confusion majeur pour les programmeurs.
REMARQUE: De nombreux manuels de logiciels utilisent en fait le terme «thread-safe» pour désigner «compatible thread», ajoutant encore plus de confusion à ce qui était déjà un gâchis! J'évite à tout prix les termes "thread-safe" et "thread-unsafe", car certaines sources appellent quelque chose "thread-safe" tandis que d'autres l'appelleront "thread-unsafe" parce qu'elles ne peuvent pas être d'accord si vous devez répondre à des normes de sécurité supplémentaires (primitives de synchronisation), ou simplement NE PAS être hostile pour être considéré comme "sûr". Évitez donc ces termes et utilisez plutôt les termes les plus intelligents, pour éviter de mauvaises communications dangereuses avec d'autres ingénieurs.
Rappel de nos objectifs
Essentiellement, notre objectif est de renverser le chaos.
Nous le faisons en créant des systèmes déterministes sur lesquels nous pouvons compter. Le déterminisme est coûteux, principalement en raison des coûts d'opportunité de la perte du parallélisme, du pipelining et de la réorganisation. Nous essayons de minimiser la quantité de déterminisme dont nous avons besoin pour maintenir nos coûts bas, tout en évitant de prendre des décisions qui éroderont davantage le peu de déterminisme que nous pouvons nous permettre.
La synchronisation des threads consiste à augmenter l'ordre et à réduire le chaos. Les niveaux auxquels vous le faites correspondent aux conditions mentionnées ci-dessus. Le niveau le plus élevé signifie qu'un système se comporte de manière entièrement prévisible à chaque fois. Le deuxième niveau signifie que le système se comporte suffisamment bien pour que le code appelant puisse détecter de manière fiable l'imprévisibilité. Par exemple, un réveil parasite dans une variable de condition ou un échec de verrouillage d'un mutex car il n'est pas prêt. Le troisième niveau signifie que le système ne se comporte pas assez bien pour jouer avec quelqu'un d'autre et ne peut être exécuté que sur un seul thread sans provoquer de chaos.
la source
Au lieu de considérer le code ou les classes comme thread-safe ou non, je pense qu'il est plus utile de penser aux actions comme étant thread-safe. Deux actions sont thread-safe si elles se comportent comme spécifié lors de l'exécution à partir de contextes de thread arbitraires. Dans de nombreux cas, les classes prendront en charge certaines combinaisons d'actions de manière thread-safe et d'autres non.
Par exemple, de nombreuses collections telles que les listes de tableaux et les ensembles de hachage garantissent que si elles sont initialement accessibles exclusivement avec un seul thread, et qu'elles ne sont jamais modifiées après qu'une référence devient visible par d'autres threads, elles peuvent être lues de manière arbitraire par n'importe quelle combinaison de fils sans interférence.
Plus intéressant, certaines collections de jeux de hachage, comme celle non générique d'origine dans .NET, peuvent offrir la garantie que tant qu'aucun élément n'est supprimé, et à condition qu'un seul thread leur écrive, tout thread qui essaie de read la collection se comportera comme si l'accès à une collection où les mises à jour pouvaient être retardées et se produire dans un ordre arbitraire, mais qui se comporterait autrement normalement. Si le thread # 1 ajoute X puis Y, et que le thread # 2 recherche et voit Y puis X, il serait possible pour le thread # 2 de voir que Y existe mais X pas; le fait que ce comportement soit ou non «thread-safe» dépendra de la préparation du thread # 2 pour faire face à cette possibilité.
Enfin, certaines classes - en particulier les bibliothèques de communication bloquantes - peuvent avoir une méthode "close" ou "Dispose" qui est thread-safe par rapport à toutes les autres méthodes, mais aucune autre méthode qui est thread-safe par rapport à L'une et l'autre. Si un thread exécute une requête de lecture bloquante et qu'un utilisateur du programme clique sur "annuler", il n'y aurait aucun moyen pour qu'une requête de fermeture soit émise par le thread qui tente d'effectuer la lecture. Cependant, la demande de fermeture / suppression peut définir un indicateur de manière asynchrone, ce qui entraînera l'annulation de la demande de lecture dès que possible. Une fois la fermeture effectuée sur n'importe quel thread, l'objet deviendrait inutile et toutes les tentatives d'actions futures échoueraient immédiatement,
la source
En termes simples: P S'il est sûr d'exécuter plusieurs threads sur un bloc de code, il est thread safe *
* des conditions s'appliquent
Les conditions sont mentionnées par d'autres réponses comme 1. Le résultat devrait être le même si vous exécutez un thread ou plusieurs threads dessus, etc.
la source