这是一个例子。我有一个生产者和几个消费者。
#!/usr/bin/env python2
from multiprocessing import Process, Queue
import time
def counter(low, high):
current = low
while current <= high:
yield current
current += 1
def put_tasks(q):
for c in counter(0, 9):
q.put(c)
time.sleep(.1)
print('put_tasks: no more tasks')
def work(id, q):
while True:
task = q.get()
print('process %d: %s' % (id, task))
time.sleep(.3)
print('process %d: done' % id)
if __name__ == '__main__':
q = Queue(2)
task_gen = Process(target=put_tasks, args=(q,))
processes = [Process(target=work, args=(id, q)) for id in range(0, 3)]
task_gen.start()
for p in processes:
p.start()
for p in processes:
p.join()
counter
只是put_tasks
的数字生成器。通常,我会有几千个任务而不是像本例中的10个任务。这段代码的重点是逐步向队列提供任务。
问题在于消费者无法预先知道他们将要处理多少任务,但put_tasks
函数确实知道它何时完成(然后打印no more tasks
)。
示例输出:
process 2: 0
process 0: 1
process 1: 2
process 2: 3
process 0: 4
process 1: 5
process 2: 6
process 0: 7
process 1: 8
process 2: 9
put_tasks: no more tasks
所有任务都会被处理,但程序会挂起(每个进程都会卡在q.get()
上。我希望它在处理完所有任务时终止而不会牺牲速度或安全性(没有丑陋的超时)。
有什么想法吗?
答案 0 :(得分:3)
最简单的方法是向队列添加一些告诉消费者所有工作已完成的内容。
number_of_consumers = 3
def put_tasks(q):
for c in counter(0, 9):
q.put(c)
time.sleep(.1)
print('put_tasks: no more tasks')
for i in range(number_of_consumers):
q.put(None)
def work(id, q):
while True:
task = q.get()
if task is None:
break
print('process %d: %s' % (id, task))
time.sleep(.3)
print('process %d: done' % id)
答案 1 :(得分:3)
我建议将一个sentinel值放在队列的末尾
def put_tasks(q):
...
print('put_tasks: no more tasks')
q.put(end_of_queue)
def work(id, q):
while True:
task = q.get()
if task == end_of_queue:
q.put(task)
print("DONE")
return
print('process %d: %s' % (id, task))
time.sleep(.1)
print('process %d: done' % id)
class Sentinel:
def __init__(self, id):
self.id = id
def __eq__(self, other):
if isinstance(other, Sentinel):
return self.id == other.id
return NotImplemented
if __name__ == '__main__':
q = Queue(2)
end_of_queue = Sentinel("end of queue")
task_gen = Process(target=put_tasks, args=(q,))
processes = [Process(target=work, args=(id, q)) for id in range(0, 3)]
...
我似乎无法使用object()
作为标记,因为线程似乎可以访问不同的实例,因此它们不能比较相等。
如果您希望生成随机标记,可以使用uuid
模块生成随机ID:
import uuid
class Sentinel:
def __init__(self):
self.id = uuid.uuid4()
def __eq__(self, other):
if isinstance(other, Sentinel):
return self.id == other.id
return NotImplemented
最后,zch使用None
作为哨兵,只要队列中没有None
,这就完全足够了。sentinel方法适用于大多数任意参数。
答案 2 :(得分:0)
我最近查看了同样的问题,并在Python文档中找到了上面的替代答案
看起来“正确”的方法是使用Queue.task_done()
方法,即:
def worker():
while True:
item = q.get()
do_work(item)
q.task_done()
q = Queue()
for i in range(num_worker_threads):
t = Thread(target=worker)
t.daemon = True
t.start()
for item in source():
q.put(item)
q.join() # block until all tasks are done