2010-04-12 4 views

Répondre

54

Voyons voir:

>>> x = 1 
>>> y = 2 
>>> def swap_xy(): 
... global x, y 
... (x, y) = (y, x) 
... 
>>> dis.dis(swap_xy) 
    3   0 LOAD_GLOBAL    0 (y) 
       3 LOAD_GLOBAL    1 (x) 
       6 ROT_TWO    
       7 STORE_GLOBAL    1 (x) 
      10 STORE_GLOBAL    0 (y) 
      13 LOAD_CONST    0 (None) 
      16 RETURN_VALUE  

Il ne semble pas qu'ils sont atomiques: les valeurs de x et y pourrait être changé par un autre fil entre les bytecodes LOAD_GLOBAL, avant ou après le ROT_TWO, et entre les octets STORE_GLOBAL.

Si vous voulez échanger deux variables atomiquement, vous aurez besoin d'un verrou ou d'un mutex.

Pour ceux qui désirent une preuve empirique:

>>> def swap_xy_repeatedly(): 
... while 1: 
...  swap_xy() 
...  if x == y: 
...  # If all swaps are atomic, there will never be a time when x == y. 
...  # (of course, this depends on "if x == y" being atomic, which it isn't; 
...  # but if "if x == y" isn't atomic, what hope have we for the more complex 
...  # "x, y = y, x"?) 
...  print 'non-atomic swap detected' 
...  break 
... 
>>> t1 = threading.Thread(target=swap_xy_repeatedly) 
>>> t2 = threading.Thread(target=swap_xy_repeatedly) 
>>> t1.start() 
>>> t2.start() 
>>> non-atomic swap detected 
4

Oui, oui, oui.

I stand corrected.

Kragen Sitaker écrit:

Quelqu'un a recommandé d'utiliser l'idiome

spam, eggs = eggs, spam 

pour obtenir un échange de thread-safe. Est-ce que cela fonctionne vraiment? (...)
Donc, si ce thread perd contrôle en quelque lieu entre le premier LOAD_FAST
et le dernier STORE_FAST, une valeur peut être stockés par un autre thread
dans « b » qui serait alors perdu. Il n'y a rien qui empêche
de se produire, n'est-ce pas?

Nope. En général, pas même une simple affectation est nécessairement thread sûr depuis la réalisation de l'affectation peut invoquer des méthodes spéciales sur un objet qui eux-mêmes peuvent nécessiter un numéro des opérations. Espérons que l'objet aura verrouillé en interne ses valeurs "state", mais ce n'est pas toujours le cas.

Mais il est vraiment dicté par ce que « sécurité des threads » signifie dans une application particulière, car à mon avis il ya beaucoup de niveaux de granularité de cette sécurité il est donc difficile de parler de « fil de sécurité ». A propos de la seule chose l'interpréteur Python va à vous donner gratuitement est qu'un intégré type de données devrait être sûr de corruption interne, même avec le filetage natif. En d'autres termes, si deux fils ont a=0xff et a=0xff00, un finira par avec l'un ou l'autre, mais pas accidentellement 0xffff comme cela pourrait être possible dans d'autres langues si un n'est pas protégé.

Cela dit, Python tend également à exécuter de telle façon que vous pouvez sortir avec un très grand nombre sans verrouillage formel, si vous êtes prêt à vivre sur le bord un peu et avoir dépendances implicites sur les objets utilisés. Il y avait une discussion décente le long de ces lignes ici c.l.p un peu en arrière - recherche groups.google.com pour le fil "Critical sections et mutex" parmi autres.

personnellement I verrouiller explicitement partagé état (ou utiliser des produits d'assemblage conçus pour échanger des informations partagées correctement entre les fils, tels que Queue.Queue) dans une application multi-threads. Pour mon esprit c'est la meilleure protection contre la maintenance et l'évolution vers le bas la route.

- - David

+1

Pourquoi? GIL? Le démontage ne suggère pas d'atomicité (voir la réponse de @ jemfinch). – kennytm

+0

(BTW, le commentaire ci-dessus n'est * pas * une question rhétorique.) – kennytm

+0

@Kenny: c'était un malentendu de ma part que la façon dont le déballage de tuple fonctionnait au niveau bas. – voyager