Lo que busca es un patrón de productor / consumidor
Ejemplo de subprocesamiento básico
Aquí hay un ejemplo básico usando el módulo de subprocesamiento (en lugar de multiprocesamiento)
import threading
import Queue
import sys
def do_work(in_queue, out_queue):
while True:
item = in_queue.get()
result = item
out_queue.put(result)
in_queue.task_done()
if __name__ == "__main__":
work = Queue.Queue()
results = Queue.Queue()
total = 20
for i in xrange(4):
t = threading.Thread(target=do_work, args=(work, results))
t.daemon = True
t.start()
for i in xrange(total):
work.put(i)
work.join()
for i in xrange(total):
print results.get()
sys.exit()
No compartirías el objeto de archivo con los hilos. Produciría trabajo para ellos proporcionando a la cola líneas de datos. Luego, cada hilo tomaría una línea, la procesaría y luego la devolvería a la cola.
Hay algunas funciones más avanzadas integradas en el módulo de multiprocesamiento para compartir datos, como listas y tipos especiales de cola . Existen ventajas y desventajas al utilizar multiprocesamiento frente a subprocesos y depende de si su trabajo está vinculado a la CPU o IO.
Multiprocesamiento básico Ejemplo de agrupación
Aquí hay un ejemplo realmente básico de un grupo de multiprocesamiento
from multiprocessing import Pool
def process_line(line):
return "FOO: %s" % line
if __name__ == "__main__":
pool = Pool(4)
with open('file.txt') as source_file:
results = pool.map(process_line, source_file, 4)
print results
Un Pool es un objeto de conveniencia que gestiona sus propios procesos. Dado que un archivo abierto puede iterar sobre sus líneas, puede pasarlo a pool.map()
, que lo recorrerá y entregará líneas a la función de trabajo. Map bloquea y devuelve el resultado completo cuando está hecho. Tenga en cuenta que este es un ejemplo demasiado simplificado y que pool.map()
va a leer todo el archivo en la memoria de una vez antes de realizar el trabajo. Si espera tener archivos grandes, tenga esto en cuenta. Hay formas más avanzadas de diseñar una configuración de productor / consumidor.
"Pool" manual con límite y reordenación de líneas
Este es un ejemplo manual de Pool.map , pero en lugar de consumir un iterable completo de una sola vez, puede establecer un tamaño de cola para que solo lo alimente pieza por pieza tan rápido como pueda procesar. También agregué los números de línea para que pueda rastrearlos y consultarlos si lo desea, más adelante.
from multiprocessing import Process, Manager
import time
import itertools
def do_work(in_queue, out_list):
while True:
item = in_queue.get()
line_no, line = item
if line == None:
return
time.sleep(.5)
result = (line_no, line)
out_list.append(result)
if __name__ == "__main__":
num_workers = 4
manager = Manager()
results = manager.list()
work = manager.Queue(num_workers)
pool = []
for i in xrange(num_workers):
p = Process(target=do_work, args=(work, results))
p.start()
pool.append(p)
with open("source.txt") as f:
iters = itertools.chain(f, (None,)*num_workers)
for num_and_line in enumerate(iters):
work.put(num_and_line)
for p in pool:
p.join()
print sorted(results)