2012-03-01 12 views
5

Decir que tengo una lista l y un hilo t1 interactuando sobre l para siempre¿Python congela la lista antes de for-loop?

while True: 
    for i in l: 
     #do something 

y otro hilo t2 azar modificar o eliminar miembros en l.

¿Qué sucede después de una eliminación? ¿Lo detecta t1 en el bucle de corriente?

ACTUALIZACIÓN:

  1. Por congelación quiero decir t1 obtener una copia de l. t2 puede modificar para l Seguro

  2. citando documentación o simple pero persuadir fragmento de código se bienvenida.

+0

¿Has * probado *? –

+2

@ChrisMorgan Los errores de condición de carrera son muy difíciles de encontrar. Por lo tanto, intentarlo solo puede mostrar que hay un error obvio y que no puede mostrar lo contrario. Peor aún, el GIL de cpython ocultará los errores consistentemente. – phihag

+2

@ChrisMorgan Se me ocurre esto en mi proyecto actual, pero esto no desencadena un error fatal y no puedo encontrar un ejemplo simple pero persuasivo para revelar la verdad. – onemach

Respuesta

7

No. La lista no está congelada: su iterador no se "romperá" en el sentido de generar una excepción. En cambio, seguirá avanzando en la lista, con resultados que posiblemente sean sorprendentes.

Considere este código (fragmento situado Ideone aquí: http://ideone.com/0iMao):

l = list(range(10)) 
for i in l: 
    print i 
    try: 
     del l[i] 
    except (RuntimeError,IndexError), e: 
     print e 

print l 

produce esta salida:

0 
2 
4 
5 
7 
list assignment index out of range 
9 
list assignment index out of range 
[1, 2, 4, 5, 7, 9] 

que probablemente no es lo que quería o esperaba, aunque es aparentemente bien -definido: http://docs.python.org/reference/compound_stmts.html#the-for-statement.

En su lugar, puede bloquear la lista o tomar una copia. Tenga en cuenta que iter(l) no toma una copia internamente, y tendrá el mismo efecto que simplemente iterar sobre la lista directamente.

+1

+1 para dar frío ejemplo – Simon

0

la lista es accesible desde ambos hilos y no está congelada. el iterador "sabrá" que los miembros se han eliminado y se los saltará, a menos que tenga acceso explícito al índice más allá de la longitud de la lista. las eliminaciones son seguras para subprocesos.

si desea un bloqueo real de la lista, protéjala con un mutex o cópiela.

2

Aquí es el tipo de cosas se pueden observar:

>>> from threading import Thread 
>>> from time import sleep 
>>> liszt = ['first item', 'second item', 'third item', 'fourth item', 
...   'plentee more items', "but I'm lazy"] 
>>> def thread_one(): 
...  for i in liszt: 
...    print 'Thread one found "%s"' % i 
...    sleep(1) 
... 
>>> def thread_two(): 
...  sleep(0.5) 
...  print 'Thread two deleting first item.' 
...  del liszt[0] 
...  sleep(1) 
...  print 'Thread two deleting fourth item.' 
...  del liszt[3] 
... 
>>> Thread(target=thread_one).start(); Thread(target=thread_two).start() 
Thread one found "first item" 
Thread two deleting first item. 
Thread one found "third item" 
Thread two deleting fourth item. 
Thread one found "fourth item" 
Thread one found "but I'm lazy" 

De esto se puede ver que la modificación de la lista en un único hilo afectó el iterador en el otro hilo; Al eliminar el primer elemento, el iterador omitió un elemento y, al eliminar un elemento futuro, no se verá en el iterador porque ya no está.

Aquí hay un modelo de cómo funciona esto; No estoy brindando código para esto de manera explícita, pero puedes resolverlo a partir de la observación.

State:    [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] 
Iterator position:^

Ir al artículo siguiente.

State:    [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] 
Iterator position: ^

Borrar el primer elemento.

State:    [2, 3, 4, 5, 6, 7, 8, 9, 10] 
Iterator position: ^

Ir al artículo siguiente.

State:    [2, 3, 4, 5, 6, 7, 8, 9, 10] 
Iterator position:  ^

Eliminar el quinto elemento.

State:    [2, 3, 4, 5, 7, 8, 9, 10] 
Iterator position:  ^

Et cetera. En el tema de enhebrar, no importa si lo haces en un hilo o en varios hilos. Claro, puede tener una condición de carrera sobre si el elemento se borra o no, pero sigue funcionando de la misma manera.

Si no está familiarizado con las partes internas de la iteración, conozca iter. for x in y itera sobre iter(y), en realidad. Por lo tanto, puede jugar con el objeto de lista de elementos iter(liszt) si lo desea, usando next() en él mientras juega con la lista que está iterando. Más conveniente que un for-loop en la consola interactiva de Python.