En Python en particulier, comment les variables sont-elles partagées entre les threads?
Bien que j'aie utilisé threading.Thread
avant, je n'ai jamais vraiment compris ou vu des exemples de partage des variables. Sont-ils partagés entre le fil principal et les enfants ou seulement entre les enfants? Quand aurais-je besoin d'utiliser le stockage local des threads pour éviter ce partage?
J'ai vu de nombreux avertissements sur la synchronisation de l'accès aux données partagées entre les threads à l'aide de verrous, mais je n'ai pas encore vu un très bon exemple du problème.
Merci d'avance!
Réponses:
En Python, tout est partagé, à l'exception des variables locales de fonction (car chaque appel de fonction obtient son propre ensemble de paramètres locaux et les threads sont toujours des appels de fonction séparés.) Et même dans ce cas, seules les variables elles-mêmes (les noms qui font référence aux objets) sont locaux à la fonction; les objets eux-mêmes sont toujours globaux et tout peut s'y référer. L'
Thread
objet d'un thread particulier n'est pas un objet spécial à cet égard. Si vous stockez l'Thread
objet à un endroit accessible à tous les threads (comme une variable globale), tous les threads peuvent accéder à cetThread
objet. Si vous souhaitez modifier de manière atomique tout ce à quoi un autre thread a accès, vous devez le protéger avec un verrou. Et tous les threads doivent bien sûr partager ce même verrou, sinon ce ne serait pas très efficace.Si vous voulez un stockage réel local des threads, c'est là
threading.local
qu'intervient. Les attributs dethreading.local
ne sont pas partagés entre les threads; chaque thread ne voit que les attributs qu'il y a lui-même placés. Si vous êtes curieux de connaître sa mise en œuvre, la source se trouve dans _threading_local.py dans la bibliothèque standard.la source
Considérez le code suivant:
Ici, threading.local () est utilisé comme un moyen rapide et sale de passer des données de run () à bar () sans changer l'interface de foo ().
Notez que l'utilisation de variables globales ne fera pas l'affaire:
En attendant, si vous pouviez vous permettre de passer ces données comme argument de foo (), ce serait un moyen plus élégant et bien conçu:
Mais ce n'est pas toujours possible lors de l'utilisation de code tiers ou mal conçu.
la source
Vous pouvez créer un stockage local de thread à l'aide de
threading.local()
.Les données stockées dans le tls seront uniques à chaque thread, ce qui aidera à garantir qu'aucun partage involontaire ne se produira.
la source
Comme dans tous les autres langages, chaque thread en Python a accès aux mêmes variables. Il n'y a pas de distinction entre le «thread principal» et les threads enfants.
Une différence avec Python est que le Global Interpreter Lock signifie qu'un seul thread peut exécuter du code Python à la fois. Cependant, cela n'aide pas beaucoup pour la synchronisation de l'accès, car tous les problèmes de préemption habituels s'appliquent toujours et vous devez utiliser des primitives de thread comme dans d'autres langues. Cela signifie toutefois que vous devez reconsidérer si vous utilisiez des threads pour les performances.
la source
Je me trompe peut-être ici. Si vous savez le contraire, veuillez expliquer car cela aiderait à expliquer pourquoi il faudrait utiliser thread local ().
Cette déclaration semble éteinte, pas fausse: "Si vous voulez modifier de manière atomique tout ce à quoi un autre thread a accès, vous devez le protéger avec un verrou." Je pense que cette déclaration est -> effectivement <- correcte mais pas tout à fait exacte. Je pensais que le terme "atomique" signifiait que l'interpréteur Python créait un bloc d'octet-code qui ne laissait aucune place pour un signal d'interruption au CPU.
Je pensais que les opérations atomiques sont des morceaux de code octet Python qui ne donnent pas accès aux interruptions. Les instructions Python comme "running = True" sont atomiques. Vous n'avez pas besoin de verrouiller le CPU des interruptions dans ce cas (je crois). La ventilation du code d'octet Python est à l'abri de l'interruption de thread.
Le code Python comme "threads_running [5] = True" n'est pas atomique. Il y a deux morceaux de code d'octet Python ici; un pour dé-référencer la liste () pour un objet et un autre morceau de code d'octet pour attribuer une valeur à un objet, dans ce cas un "endroit" dans une liste. Une interruption peut être déclenchée -> entre <- les deux octets-code -> morceaux <-. C'est là que de mauvaises choses se produisent.
Quel est le rapport entre thread local () et "atomic"? C'est pourquoi la déclaration me semble mal orientée. Sinon, pouvez-vous expliquer?
la source