由于我在工作中使用了类似的模式,因此我决定编写一个类,通过作业队列/线程来抽象非常简单的工作并发。我知道已经有一些东西可以解决这个问题,但我也希望利用这个机会来磨练我的多线程技能。
我给自己的主要挑战是,我希望这能让流程完成,即使它们没有被Queue.join()明确阻止。 "流程结束"由输入函数定义返回值(或None)。我试图实现这一目标的方法是让每个作业创建自己的结果队列rq
,然后在非守护程序线程中由_wait_for_results
检查,这会阻止自动退出rq
中的工作人员填写add_to_queue
之前的所有其他守护程序线程。
以下是完整的课程:
class EasyPool(object):
def __init__(self, concurrency, always_finish=True):
def add_to_queue(q):
while True:
func_data, rq = q.get()
func, args, kwargs = func_data
if not args:
args = []
if not kwargs:
kwargs = {}
result = func(*args, **kwargs)
rq.put(result)
q.task_done()
self.rqs = []
self.always_finish = always_finish
self.q = Queue(maxsize=0)
self.workers = []
for i in range(concurrency):
worker = Thread(target=add_to_queue, args=(self.q,))
self.workers.append(worker)
worker.setDaemon(True)
worker.start()
def _wait_for_results(self, rq):
rq.not_empty.acquire()
rq.not_empty.wait()
rq.not_empty.notify()
rq.not_empty.release()
def add_job(self, func, *args, **kwargs):
rq = Queue()
if self.always_finish:
blocker = Thread(target=self._wait_for_results, args=(rq,))
blocker.setDaemon(False)
blocker.start()
to_add = []
[ to_add.append(i) if i else to_add.append(None) for i in [func, args, kwargs] ]
self.q.put((to_add, rq))
return rq.get
当通过.add_job
实例方法创建作业时,它会立即返回类似于promise的对象,该对象是对结果队列的.get
方法的引用。我面临的问题是,.get
和_wait_for_results
方法之间似乎存在竞争条件。我认为答案可能涉及锁定或条件,但我不确定。非常感谢任何帮助:)