我试图看看是否有可能采用通用(串行)算法,并通过简单地对关键函数进行注释,使它们以并行和分布式方式执行。目标:
这对于简单的任务来说是微不足道的,但对于我们也要分发的具有相关计算的任务而言,却并不明显。假设我有任务:
@distributed(pool)
def task1()
# Task1 has dependent, distributed computation
# Cannot return until a and b are both computed by subtasks
a = task2(11)
b = task2(22)
# Continue and do more processing, which may be
# complex and time consuming:
result = a + b
return result
@distributed(pool)
def task2(x): .... return x*10
@distributed注释负责对包装的任务函数及其参数的调用的序列化,并返回结果。
上面的问题是:
a = task2(11)
b = task2(22)
会顺序提交到池中,并且不会从并行性中受益。因此,假设我们以两种方式之一对其进行修复:
@distributed
返回一个future,并且直到计算结果的值时才阻塞。 (我们必须致电a.await()+b.await()
)或
我们明确引用了该池:
a,b = pool.map(task2, [(11,), (22,)])
and pool.map知道,当看到@distributed时,它应该“做正确的事”并分发包装的函数。
好的,要求(1)有点软,但到目前为止,已经足够简单了。
此模式的困难在于,如果pool
仅具有2个可用工人,而我提交了2x task1
,则当task2
提交给池时,我们可能出现死锁:task2
无法执行,因为两个工作人员都在忙于执行task1
并等待其结果。
什么是解决此问题的好模式,这样我可以有一个(分布式)工作池来执行我的所有作业,而对代码的入侵最少。
常见解决方案:
每个工作人员一个主题(/任务类型):每种工作类型都有一组工作人员可以避免在上述情况下出现死锁,但是在递归任务(例如决策树构建)的一般情况下,则不会。这也很麻烦,因为我们必须定义哪些工人负责哪些任务(失败3)。基于Akka和AMPQ的系统似乎可以这种方式工作。
Theadpools可以解决死锁问题:
我觉得我在这里缺少一个好的模式,这将帮助我避免重构很多可分发的代码。