¿Son los generadores Threadsafe?

Tengo un progtwig multiproceso en el que creo una función de generador y luego la paso a nuevos subprocesos. Quiero que sea de naturaleza compartida / global para que cada hilo pueda obtener el siguiente valor del generador.

¿Es seguro usar un generador como este, o tendré problemas / condiciones para acceder al generador compartido desde varios subprocesos?

Si no es así, ¿hay una mejor manera de abordar el problema? Necesito algo que pase por una lista y produzca el siguiente valor para el hilo que lo llame.

No es seguro para subprocesos; Las llamadas simultáneas pueden intercalarse y desordenarse con las variables locales.

El enfoque común es usar el patrón maestro-esclavo (ahora llamado patrón de agricultor-trabajador en PC). Haga un tercer hilo que genere datos y agregue una Cola entre el maestro y los esclavos, donde los esclavos leerán de la cola y el maestro escribirá en ella. El módulo de cola estándar proporciona la seguridad necesaria para los hilos y permite bloquear el maestro hasta que los esclavos estén listos para leer más datos.

Editado para agregar punto de referencia a continuación.

Puedes envolver un generador con una cerradura. Por ejemplo,

import threading class LockedIterator(object): def __init__(self, it): self.lock = threading.Lock() self.it = it.__iter__() def __iter__(self): return self def next(self): self.lock.acquire() try: return self.it.next() finally: self.lock.release() gen = [x*2 for x in [1,2,3,4]] g2 = LockedIterator(gen) print list(g2) 

El locking tarda 50 ms en mi sistema, la cola tarda 350 ms. La cola es útil cuando realmente tienes una cola; por ejemplo, si tiene solicitudes HTTP entrantes y desea ponerlas en cola para ser procesadas por subprocesos de trabajo. (Eso no encaja en el modelo de iterador de Python: una vez que un iterador se queda sin elementos, está listo). Si realmente tiene un iterador, entonces LockedIterator es una forma más rápida y sencilla de hacerlo seguro.

 from datetime import datetime import threading num_worker_threads = 4 class LockedIterator(object): def __init__(self, it): self.lock = threading.Lock() self.it = it.__iter__() def __iter__(self): return self def next(self): self.lock.acquire() try: return self.it.next() finally: self.lock.release() def test_locked(it): it = LockedIterator(it) def worker(): try: for i in it: pass except Exception, e: print e raise threads = [] for i in range(num_worker_threads): t = threading.Thread(target=worker) threads.append(t) t.start() for t in threads: t.join() def test_queue(it): from Queue import Queue def worker(): try: while True: item = q.get() q.task_done() except Exception, e: print e raise q = Queue() for i in range(num_worker_threads): t = threading.Thread(target=worker) t.setDaemon(True) t.start() t1 = datetime.now() for item in it: q.put(item) q.join() start_time = datetime.now() it = [x*2 for x in range(1,10000)] test_locked(it) #test_queue(it) end_time = datetime.now() took = end_time-start_time print "took %.01f" % ((took.seconds + took.microseconds/1000000.0)*1000) 

No, no son seguros para los hilos. Puede encontrar información interesante sobre generadores y subprocesos múltiples en:

http://www.dabeaz.com/generators/Generators.pdf

Depende de la implementación de Python que estés usando. En CPython, GIL hace que todas las operaciones en los objetos de Python sean seguras para subprocesos, ya que solo un hilo puede ejecutar código en un momento dado.

http://en.wikipedia.org/wiki/Global_Interpreter_Lock