Quelle est la signification du terme «thread-safe»?

368

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?

Varun Mahajan
la source
8
Je viens de voir une discussion intéressante à ce sujet: blogs.msdn.com/ericlippert/archive/2009/10/19/…
Sebastian

Réponses:

257

De Wikipédia:

La sécurité des threads est un concept de programmation informatique applicable dans le contexte des programmes multithreads. Un morceau de code est thread-safe s'il fonctionne correctement lors de l'exécution simultanée par plusieurs threads. En particulier, il doit satisfaire le besoin de plusieurs threads pour accéder aux mêmes données partagées et le besoin pour un morceau de données partagé d'être accédé par un seul thread à un moment donné.

Il existe plusieurs façons d'assurer la sécurité des threads:

Rentrée:

Écrire du code de telle manière qu'il puisse être partiellement exécuté par une tâche, ré-entré par une autre tâche, puis repris à partir de la tâche d'origine. Cela nécessite la sauvegarde des informations d'état dans des variables locales à chaque tâche, généralement sur sa pile, au lieu de variables statiques ou globales.

Exclusion mutuelle:

L'accès aux données partagées est sérialisé à l'aide de mécanismes qui garantissent qu'un seul thread lit ou écrit les données partagées à tout moment. Une grande prudence est requise si un morceau de code accède à plusieurs données partagées - les problèmes incluent les conditions de concurrence, les blocages, les verrous de survie, la famine et divers autres maux énumérés dans de nombreux manuels de systèmes d'exploitation.

Stockage local par thread:

Les variables sont localisées afin que chaque thread ait sa propre copie privée. Ces variables conservent leurs valeurs à travers les sous-programmes et autres limites de code et sont thread-safe car elles sont locales à chaque thread, même si le code qui y accède peut être réentrant.

Opérations atomiques:

Les données partagées sont accessibles en utilisant des opérations atomiques qui ne peuvent pas être interrompues par d'autres threads. Cela nécessite généralement l'utilisation d'instructions spéciales en langage machine, qui peuvent être disponibles dans une bibliothèque d'exécution. Étant donné que les opérations sont atomiques, les données partagées sont toujours conservées dans un état valide, quels que soient les autres threads qui y accèdent. Les opérations atomiques sont à la base de nombreux mécanismes de verrouillage des threads.

Lire la suite:

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


Blauohr
la source
4
Ce lien manque techniquement plusieurs points critiques. La mémoire partagée en question doit être modifiable (la mémoire morte ne peut pas être non sécurisée pour les threads) et les threads multiples doivent état, et b) permettre à d'autres threads d'interrompre ce thread alors que la mémoire est incohérente.
Charles Bretana
20
lorsque Google recherche le premier résultat est wiki, inutile de le rendre redondant ici.
Ranvir
Que voulez-vous dire par "un code accédant à une fonction"? La fonction en cours d'exécution est le code, n'est-ce pas?
Koray Tugay
82

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

Marek Blotny
la source
35
Dans le même processus!
Ali Afshar
En effet, dans le même processus :)
Marek Blotny
4
"Pour écrire du code qui fonctionnera de manière stable pendant des semaines, il faut une paranoïa extrême." C'est une citation que j'aime :)
Jim T
6
duh! cette réponse ne fait que reformuler la question! --- Et pourquoi seulement dans le même processus ??? Si le code échoue lorsque plusieurs threads l'exécutent à partir de processus différents, alors, sans doute (la "mémoire partagée" peut être dans un fichier disque), il n'est PAS sûr pour les threads !!
Charles Bretana
1
@ mg30rg. Peut-être la confusion est-elle le résultat de la pensée en quelque sorte que lorsqu'un bloc de code est exécuté par plusieurs processus, mais seulement par un thread par processus, que cela reste en quelque sorte un scénario "à thread unique", pas un scénario à threads multiples . Cette idée n'est même pas fausse. C'est juste une mauvaise définition. De toute évidence, plusieurs processus ne s'exécutent généralement pas sur le même thread de manière synchronisée, (sauf dans de rares cas où les processus par conception se coordonnent entre eux et le système d'exploitation partage les threads entre les processus.)
Charles Bretana
50

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)

totalRequests = totalRequests + 1
MOV EAX, [totalRequests]   // load memory for tot Requests into register
INC EAX                    // update register
MOV [totalRequests], EAX   // store updated value back to memory
  1. La première condition est qu'il existe des emplacements de mémoire accessibles à partir de plusieurs threads. En règle générale, ces emplacements sont des variables globales / statiques ou de la mémoire de tas accessible à partir de variables globales / statiques. Chaque thread obtient son propre cadre de pile pour les variables locales de portée de fonction / méthode, donc ces variables de fonction / méthode locales, otoh, (qui sont sur la pile) sont accessibles uniquement à partir du seul thread qui possède cette pile.
  2. La deuxième condition est qu'il existe une propriété (souvent appelée invariante ), associée à ces emplacements de mémoire partagée, qui doit être vraie ou valide pour que le programme fonctionne correctement. Dans l'exemple ci-dessus, la propriété est que « totalRequests doit représenter avec précision le nombre total de fois qu'un thread a exécuté une partie de l'instruction d'incrémentation ». En règle générale, cette propriété invariante doit rester vraie (dans ce cas, totalRequests doit contenir un décompte précis) avant qu'une mise à jour ne se produise pour que la mise à jour soit correcte.
  3. La troisième condition est que la propriété invariante ne tient pas pendant une partie de la mise à jour réelle. (Il est temporairement invalide ou faux pendant une partie du traitement). Dans ce cas particulier, à partir du moment où totalRequests est récupéré jusqu'au moment où la valeur mise à jour est stockée, totalRequests ne satisfait pas l'invariant.
  4. La quatrième et dernière condition qui doit se produire pour qu'une course se produise (et pour que le code ne soit donc PAS "thread-safe") est qu'un autre thread doit pouvoir accéder à la mémoire partagée pendant que l'invariant est cassé, provoquant ainsi des incohérences ou comportement incorrect.
Charles Bretana
la source
6
Cela ne couvre que ce que l'on appelle les courses de données et est bien sûr important. Pourtant, il existe d'autres façons pour lesquelles le code ne peut pas être thread-safe - par exemple un mauvais verrouillage qui peut entraîner des blocages. Même quelque chose de simple comme appeler System.exit () quelque part dans un thread java rend ce code non sûr pour les threads.
Ingo
2
Je suppose que dans une certaine mesure, c'est de la sémantique, mais je dirais qu'un mauvais code de verrouillage qui peut provoquer un blocage ne rend pas le code dangereux. Tout d'abord, il n'est pas nécessaire de verrouiller le code en premier lieu, sauf si une condition de concurrence, comme décrit ci-dessus, est possible. Ensuite, si vous écrivez le code de verrouillage de manière à provoquer un blocage, ce n'est pas dangereux pour les threads, c'est juste du mauvais code.
Charles Bretana
1
Mais notez que le blocage ne se produira pas lors de l'exécution d'un seul thread, donc pour la plupart d'entre nous, cela tomberait sûrement sous le sens intuitif de (pas) "thread-safe".
Jon Coombs
Eh bien, les blocages ne peuvent pas se produire à moins que vous exécutiez plusieurs threads bien sûr, mais c'est comme dire que les problèmes de réseau ne peuvent pas se produire si vous exécutez sur une seule machine. D'autres problèmes peuvent également se produire sur un seul thread, si le programmeur écrit le code afin qu'il se détache des lignes de code critiques avant de terminer la mise à jour et modifie la variable dans un autre sous-programme.
Charles Bretana
34

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. "

Buu Nguyen
la source
Cette définition est incomplète et non spécifique, et certainement pas exhaustive. Combien de fois doit-il fonctionner en toute sécurité, une seule fois? dix fois? à chaque fois? 80% du temps? et il ne précise pas ce qui le rend "dangereux". S'il ne s'exécute pas en toute sécurité, mais l'échec est dû à une erreur de division par zéro, cela en fait-il un thread «dangereux»?
Charles Bretana
Soyez plus courtois la prochaine fois et nous pourrons peut-être en discuter. Ce n'est pas Reddit et je ne suis pas d'humeur à parler à des gens impolis.
Buu Nguyen
Votre interprétation des commentaires sur la définition de quelqu'un d'autre comme des insultes à vous-même est révélatrice. Vous devez lire et comprendre la substance avant de réagir émotionnellement. Rien d'incivile à propos de mon commentaire. Je faisais une remarque sur le sens de la définition. Désolé si les exemples que j'ai utilisés pour illustrer ce point vous ont mis mal à l'aise.
Charles Bretana
28

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, StringBufferet StringBuilder. La différence est qu'elle StringBufferest adaptée aux threads, donc une seule instance de a StringBufferpeut être utilisée par plusieurs threads à la fois. StringBuildern'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.

Marcus Downing
la source
22

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.

Mnementh
la source
21

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.

Hapkido
la source
9

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.

Bill le lézard
la source
9

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 :

Une [partie de code] 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 supplémentaire ou autre coordination de la part du code d'appel.

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é)
  • toutes les valeurs de 0 à la valeur actuelle ont été renvoyées à un moment donné (aucune valeur n'est ignorée)

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

assylias
la source
8

Simplement - le code fonctionnera correctement si plusieurs threads exécutent ce code en même temps.

Greg Balajewicz
la source
6

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.

tvanfosson
la source
5

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 synchronizedet volatile, ainsi que les méthodes Thread.start()et Thread.join(), peuvent former des relations qui se produisent avant .

Les méthodes de toutes les classes dans java.util.concurrentet ses sous-packages étendent ces garanties à une synchronisation de niveau supérieur. En particulier:

  1. Les actions dans un thread avant de placer un objet dans une collection simultanée se produisent avant que les actions postérieures à l'accès ou à la suppression de cet élément de la collection dans un autre thread.
  2. Actions dans un thread avant la soumission d'un Runnableà un Executorévénement, avant le début de son exécution. De même pour Callables soumis à un ExecutorService.
  3. Actions prises par le calcul asynchrone représentées par des Futureactions se produisant avant la suite de la récupération du résultat via Future.get()dans un autre thread.
  4. Actions avant de "libérer" les méthodes du synchroniseur , telles que les Lock.unlock, Semaphore.release, and CountDownLatch.countDownactions se produisant avant, après une méthode "d'acquisition" réussie, comme Lock.lock, Semaphore.acquire, Condition.await, and CountDownLatch.awaitsur le même objet du synchroniseur dans un autre thread.
  5. Pour chaque paire de threads qui ont réussi à échanger des objets via un Exchanger, les actions antérieures à exchange()dans chaque thread se produisent avant celles qui suivent l'échange correspondant () dans un autre thread.
  6. Les actions avant l'appel CyclicBarrier.awaitet Phaser.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.
Ravindra babu
la source
4

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:

  1. fonctionne avec une ressource externe qui n'est pas sûre pour les threads.
  2. Lit ou modifie un objet persistant ou un champ de classe

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:

  • le grand coût d'exécution (déjà mentionné par d'autres) sur les applications qui itèrent sur ces structures "thread-safe".
  • Thread.sleep (5000) est censé dormir pendant 5 secondes. Cependant, si quelqu'un modifie l'heure du système, vous pouvez dormir très longtemps ou pas du tout. L'OS enregistre l'heure de réveil sous forme absolue et non relative.
VonC
la source
2

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.

Steve Knight
la source
2

Répondons à cela par l'exemple:

class NonThreadSafe {

    private int counter = 0;

    public boolean countTo10() {
        count = count + 1;
        return (count == 10);
    }

La countTo10mé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.

rghome
la source
1

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:

  1. fil sûr
  2. pas sûr pour les threads

Le second (et mieux) est:

  1. résistant au fil
  2. compatible avec les fils
  3. fil hostile

Un troisième est:

  1. synchronisé en interne
  2. synchronisé en externe
  3. non synchronisable

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.

Daniel Russell
la source
1

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,

supercat
la source
0

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.

minable
la source