2010-12-27 9 views
13

Cuando se trata de subprocesos, sé que debe asegurarse de que no está editando una variable al mismo tiempo que otro subproceso lo está editando, ya que sus cambios pueden perderse (al incrementar un contador, por ejemplo)Modificación de un diccionario de Python a partir de diferentes subprocesos

¿Se aplica lo mismo a los diccionarios? ¿O es un diccionario una colección de variables?

Si cada hilo fuera a bloquear el diccionario, se ralentizaría significativamente el programa, mientras que cada hilo solo necesita acceso de escritura a su pequeña porción del diccionario.

Si no es posible, ¿hay algún tipo de variable variable en python, como en php?

Respuesta

6

Creo que has entendido mal este asunto de la seguridad de todo el hilo. No se trata tanto de variables (o variables variables, son terribles de todos modos, y son tan inútiles, por no decir dañinas, aquí como en cualquier otro caso), sino sobre - por ejemplo, hay muchas maneras desagradables de enhebrar que pueden irse incorrecto; todas ellas proceden de acceder a algo mutable de más de un hilo, a veces superpuestos - esto:

  • hilo N obtiene los datos de la fuente (un lugar en la memoria o en el disco - una variable, una ranura en un diccionario, una archivo, prácticamente todo lo mutable)
  • rosca M obtiene los datos de la fuente
  • hilo N modifica los datos
  • rosca M modifica los datos
  • hilo N sobrescribe con los datos modificados fuente
  • hilo sobreescribe M fuente con los datos modificados
  • Resultado: modificaciones hilo de N se pierden/el nuevo valor compartido no se enrosque modificaciones de N en cuenta

y se aplica a los diccionarios y las variables variables (que son sólo un horrible, horrible implementación a nivel de lenguaje de dicts con claves solo de cadena) también. Para empezar, las únicas soluciones no son el uso del estado compartido (los lenguajes funcionales lo hacen al desalentar o incluso no permitir completamente la mutabilidad, y funciona bien para ellos) o agregar algún tipo de bloqueo a todo lo que se comparte (difícil de conseguir, pero si obtienes es correcto, al menos funciona correctamente). Si no hay dos subprocesos que compartan algo en ese diccionario, estás bien, pero debes separar todo, estar (un poco más) seguro de que realmente no comparten nada.

+0

hecho.Y debería thread N AND M modificaría los datos ['a'] que sucederían, pero ¿qué pasa si el hilo N modifica los datos ['a'] y el hilo M modifica los datos ['b']? ¿Eso todavía pasaría? ¿Y qué se puede hacer sin bloquear el diccionario? – skerit

+0

@skerit: Como escribí, no. No interfieren entre sí. Por supuesto, no puedes estar seguro de que nunca se superpongan si comparten el diccionario ... – delnan

+0

Oh, sí, ya veo. "una ranura en un diccionario". Entonces solo tengo que bloquear el diccionario cuando agregue o elimine una clave, ya que eso causaría excepciones cuando algo se iterara sobre ella. – skerit

0

Lo que debe hacer es no permitir que los subprocesos accedan directamente a la estructura de datos compartida, sino que en su lugar acceda a ella con algo que garantice la exclusión mutua como mutex.

Hacer que el acceso a la estructura original se vea igual (shared[id] = value) requiere algo más de trabajo, pero no tanto.

20

¿Se aplica lo mismo a los diccionarios? ¿O es un diccionario una colección de variables?

Vamos a ser más general:

¿Qué significa "operación atómica" significa?

De Wikipedia:

En la programación concurrente, una operación (o conjunto de operaciones) es atómica, linealizable, indivisibles o ininterrumpible si aparece a la resto del sistema que se produzca instantáneamente. Atomicity es una garantía de de aislamiento de los procesos concurrentes .

¿Qué significa esto en Python?

Esto significa que cada instrucción de bytecode es atómica (al menos para Python < 3.2, antes de la nueva GIL).

¿Por qué es eso ???

Porque Python (CPython) usa un Global Interpreter Lock (GIL). El intérprete CPython usa un bloqueo para asegurarse de que solo se ejecuta un hilo en el intérprete a la vez, y utiliza un "intervalo de comprobación" (ver sys.getcheckinterval()) para saber cuántas instrucciones de bytecode ejecutar antes de cambiar entre hilos (por defecto establecido en 100)

¿Y ahora qué significa esto?

Significa que las operaciones que se pueden representar con solo una instrucción de bytecode son atómicas. Por ejemplo, incrementando una variable es no atómica, ya que la operación se realiza en tres instrucciones de código de bytes:

>>> import dis 

>>> def f(a): 
     a += 1 

>>> dis.dis(f) 
    2   0 LOAD_FAST    0 (a) 
       3 LOAD_CONST    1 (1)  <<<<<<<<<<<< Operation 1 Load 
       6 INPLACE_ADD       <<<<<<<<<<<< Operation 2 iadd 
       7 STORE_FAST    0 (a)  <<<<<<<<<<<< Operation 3 store 
      10 LOAD_CONST    0 (None) 
      13 RETURN_VALUE   

¿Qué pasa con los diccionarios ??

Algunas operaciones son atómicas; por ejemplo, esta operación es atómica:

d[x] = y 
d.update(d2) 
d.keys() 

Vea usted mismo:

>>> def f(d): 
     x = 1 
     y = 1 
     d[x] = y 

>>> dis.dis(f) 
    2   0 LOAD_CONST    1 (1) 
       3 STORE_FAST    1 (x) 

    3   6 LOAD_CONST    1 (1) 
       9 STORE_FAST    2 (y) 

    4   12 LOAD_FAST    2 (y) 
      15 LOAD_FAST    0 (d) 
      18 LOAD_FAST    1 (x) 
      21 STORE_SUBSCR      <<<<<<<<<<< One operation 
      22 LOAD_CONST    0 (None) 
      25 RETURN_VALUE 

Ver this entender lo que hace STORE_SUBSCR.

Pero como se ve, no es totalmente cierto, porque esta operación:

   ... 
    4   12 LOAD_FAST    2 (y) 
      15 LOAD_FAST    0 (d) 
      18 LOAD_FAST    1 (x) 
      ... 

puede hacer toda la operación no atómica. ¿Por qué? Digamos que la variable x también puede ser cambiada por otro hilo ... o que quiere que otro hilo borre su diccionario ... podemos nombrar muchos casos cuando puede salir mal, ¡así que es complicado! Y aquí aplicaremos Murphy's Law: "Cualquier cosa que pueda salir mal, saldrá mal".

¿Y ahora qué?

Si aún desea compartir variables entre el hilo, utilice una cerradura:

import threading 

mylock = threading.RLock() 

def atomic_operation(): 
    with mylock: 
     print "operation are now atomic" 
+0

Gracias por ser minucioso. ¿Has escuchado si alguien ha implementado una capa de consistencia final además de enhebrar? Entonces, los casos que pueden tolerar datos obsoletos pueden obtener un mejor rendimiento, eso es. – HeyWatchThis

+0

@HeyWatchThis: Hola, no es que yo sepa, lo siento :(, pero probablemente ahora la gran novedad sea el hilo verde (retroceda del pasado :)) vea la biblioteca como gevent, eventlet ... que puede simplificar el enhebrado para usted especialmente si tiene un proceso de E/S enlazado, de lo contrario, todavía tiene la opción de multiprocesamiento que defiende el lenguaje más dinámico que tiene GIL (como Python, Ruby) o tal vez un nuevo brillo como memoria transaccional de pypy, solo experimental :) Else si quieres un código concurrente, mira a Erlang o tal ... Saludos, – mouad

Cuestiones relacionadas