À l'aide d'un dictionnaire mondial avec des threads en Python
Est d'y accéder/modifier le dictionnaire des valeurs thread-safe?
J'ai un mondial dictionnaire foo
et plusieurs threads avec des id id1
, id2
, ... , idn
. Est-il OK pour accéder et modifier foo
's valeurs sans allouer un cadenas pour elle si l'on sait que chaque thread ne fonctionne qu'avec l'id de la valeur liée au, dire thread avec id1
ne fonctionne qu'avec foo[id1]
?
- Vous de à l'aide Disponible, droit?
- oui, je suis de l'aide Disponible.
Vous devez vous connecter pour publier un commentaire.
En supposant Disponible: Oui et non. Il est réellement sans danger pour récupérer/sauvegarder les valeurs à partir d'un partage de dictionnaire dans le sens que simultanément plusieurs demandes de lecture/écriture de ne pas corrompre le dictionnaire. Cela est dû au mondial de l'interprète de verrouillage ("GIL") qui est géré par la mise en œuvre. Qui est:
Un Thread en cours d'exécution:
Thread B en cours d'exécution:
Thread C en cours d'exécution:
de ne pas corrompre le dictionnaire, même si tous les trois tentatives d'accès se produisent à la "même" temps. L'interprète va sérialiser dans certains undefined façon.
Cependant, les résultats de la séquence suivante n'est pas défini:
Thread:
Thread B:
comme l'essai, l'ensemble dans Un thread n'est pas atomique ("time-of-contrôle/temps d'utilisation de la" race condition). Donc, il est généralement préférable, si vous verrouillez les choses:
global_dict.setdefault("foo", 1)
dansThread A
faire de la nécessité d'un blocage inutile?a
etb
et l'invariant, c'est exactement l'un de ces champs n'est pasNone
et l'autre estNone
. Sauf si l'accès est correctement contrefil, toute combinaison aléatoire dea is [not] None
etb is [not] None
peuvent être observables en violation flagrante de l'invariant, si seulement un "naïf" getter/setter est utilisé (pensez:def set_a(self,a): self.a = a; self.b = None if a is not None else self.b
-- un des threads peut observer illégale membres au cours de l'exécution)__eq__
et__hash__
d'un objet sont mis en œuvre en C, pas en Python code de niveau.if val == a_dict.setdefault( key, val )
à atomiquement d'insertion ET de savoir si la valeur a été inséré ou existait déjà.val
était unique avant la tentative d'insertion (aucun autre thread ne peut essayer d'insérer une référence à la mêmeval
), vous pouvez utiliserif val is a_dict.setdefault(key, val):
pour effectuer l'identité de l'objet de test à identifier correctement si une mise à jour a eu lieu. Il y a quelques exceptions à la règle (sival
est internéstr
, par exemplestr
littérale, petitint
,bool
ou le videtuple
()
, c'est un singleton; ils allaient passer à l'identité de l'objet de test, même si de manière indépendante "créé" dans des lieux différents).setdefault
est mutable structures.a_dict.set_default(key, []).append(val)
est conforme en supposant que les touches ne sont jamais supprimés ou affecter de nouvelles valeurs, seulementsetdefault
-ed (ou de lire) avec la valeur résultante d'être muté en place.La meilleure, la plus sûre, portable moyen d'avoir chaque thread de travail avec des données indépendantes est:
Maintenant, chaque thread fonctionne avec une totalement indépendante
tloc
objet même si c'est un nom global. Le fil peut obtenir et définir les attributs surtloc
, utiliseztloc.__dict__
si elle spécifiquement besoin d'un dictionnaire, etc.Thread-local de stockage pour un thread s'en va à la fin du thread; des fils d'enregistrer leurs résultats définitifs, ont eux
put
leurs résultats, avant de résilier, dans une instance commune deQueue.Queue
(qui est intrinsèquement thread-safe). De même, les valeurs initiales pour les données d'un thread est de travailler sur pourrait être le passage d'arguments lorsque le thread est démarré, ou d'être pris à partir d'unQueue
.D'autres à moitié cuit, approches, comme en espérant que les opérations qui ressemblent atomique sont en effet atomique, peut arriver à travailler pour des cas spécifiques dans une version et mise à jour de Python, mais pourrait facilement être détruit par des mises à niveau ou les ports. Il n'y a pas de véritable raison du risque de tels problèmes lors de l'un de propre, propre, salubre et l'architecture y est très facile à organiser, portable, pratique, et rapide.
Depuis que j'ai besoin de quelque chose de similaire, j'ai atterri ici. Je résume vos réponses dans ce court extrait :
en tant que tel, vous pouvez utiliser le
with
construire pour tenir la serrure alors qu'il fait tripoter dans votredict()
La GIL prend soin de cela, si vous arrive d'être en utilisant
CPython
.Voir sont-serrures-inutile-dans-multi-threaded-python-code-parce-que-de-la-gil.
__hash__
méthode, de sorte que plus de 1 Python bytecode instruction est exécutée et le fil peut basculer de toute façon. Ensuite, il y a des opérations d'e/S code natif et les sections de la libération de la GIL. Les verrous sont encore très bien d'une exigence de thread-safe code.Comment ça marche?:
Chacune des instructions ci-dessus est exécuté avec GIL de verrouillage et STORE_SUBSCR instruction ajoute ou met à jour la clé+valeur de la paire dans un dictionnaire. Donc, vous voyez le dictionnaire de mise à jour est atomique et donc thread-safe.