我有一台服务器和几个客户端。它们共享一个任务并产生多处理结果.Queue。但是,每当客户端完成任务并将结果放入结果队列时,我希望服务器查看结果,并在此基础上重新排序任务队列。
这当然意味着从任务队列中弹出所有内容并重新添加。在此重新排序过程中,我希望客户端阻止触摸任务队列。我的问题是如何让服务器识别何时将任务添加到结果队列并通过锁定任务队列并在保护队列时重新排序来做出反应。不变量是服务器必须在客户端获得新任务之前返回的每个结果之后重新排序。
我认为一种简单(但错误)的方法是将multiprocessing.Value作为布尔值,每当添加结果时,客户端将其翻转为True,这意味着已添加结果。服务器可以轮询以获取此值,但最终它可能会错过另一个在轮询和添加另一个结果之间的客户端。
任何想法都赞赏。
**'多线程'标签只是因为它与线程非常相似,我不认为这里的进程/线程区别很重要。
答案 0 :(得分:1)
让我们尝试一些代码 - 一些进步比没有更好;-)部分问题是确保如果结果队列中有任何东西,任何东西都不会从任务队列中获取,对吧?所以队列密切相关。这种方法将两个队列置于锁的保护之下,并使用条件来避免任何轮询需求:
设置,在服务器中完成。必须将taskQ
,resultQ
,taskCond
和resultCond
传递给客户端进程(lock
无需显式传递 - 它包含在条件中):< / p>
import multiprocessing as mp
taskQ = mp.Queue()
resultQ = mp.Queue()
lock = mp.Lock()
# both conditions share lock
taskCond = mp.Condition(lock)
resultCond = mp.Condition(lock)
客户获得任务;所有客户都使用此功能。请注意,只要结果队列中包含某些内容,就不会使用任务:
def get_task():
taskCond.acquire()
while taskQ.qsize() == 0 or resultQ.qsize():
taskCond.wait()
# resultQ is empty and taskQ has something
task = taskQ.get()
taskCond.release()
return task
客户有结果:
with resultCond:
resultQ.put(result)
# only the server waits on resultCond
resultCond.notify()
服务器循环:
resultCond.acquire()
while True:
while resultQ.qsize() == 0:
resultCond.wait()
# operations on both queues in all clients are blocked now
# ... drain resultQ, reorder taskQ ...
taskCond.notify_all()
注意:
qsize()
通常是概率性的,但由于所有队列操作都是在锁定时完成的,因此在这种情况下它是可靠的。
实际上,因为所有队列操作都受到我们自己的锁的保护,所以实际上不需要使用mp.Queue
。例如,mp.Manager().list()
也可以工作(任何共享结构)。在重新安排任务时,列表可能更容易使用吗?
我不太喜欢的一部分:当服务器执行taskCond.notify_all()
时,某些客户端可能正在等待获取新任务,而其他客户端可能正在等待返回新结果。它们可以以任何顺序运行。只要等待返回结果的任何客户端都有机会,等待获取任务的所有客户端都将阻止,但在此之前任务将被消耗。当然,“问题”在于,在将某些内容实际添加到结果队列之前,我们不知道新结果正在等待。
对于最后一个,可能将“client has result”代码更改为:
resultQ.put(result)
with resultCond:
resultCond.notify()
会更好。不确定。它确实使得理由更加难以理解,因为所有队列操作在我们的锁定保护下完成不再是真的。