我正在尝试使用分而治之(又称fork / join)方法来解决数字运算问题。这是代码:
import scala.actors.Futures.future
private def compute( input: Input ):Result = {
if( pairs.size < SIZE_LIMIT ) {
computeSequential()
} else {
val (input1,input2) = input.split
val f1 = future( compute(input1) )
val f2 = future( compute(input2) )
val result1 = f1()
val result2 = f2()
merge(result1,result2)
}
}
它运行(具有良好的加速)但未来的apply方法似乎阻塞了一个线程并且线程池大大增加。当创建的线程太多时,计算就会停止。
是否有一种反应方法用于发布线程的期货?或者任何其他方式来实现这种行为?
编辑:我正在使用scala 2.8.0.final
答案 0 :(得分:8)
不要声明(申请)你的Future
,因为这会迫使他们阻止并等待答案;正如你所见,这可能会导致死锁。相反,单独使用它们告诉他们完成后该怎么做。而不是:
val result1 = f1()
val result2 = f2()
merge(result1,result2)
试试这个:
for {
result1 <- f1
result2 <- f2
} yield merge(result1, result2)
结果将是包含合并结果的Responder[Result]
(基本上是Future[Result]
);您可以使用respond()
或foreach()
使用此最终值执行有效的操作,也可以map()
或flatMap()
将其设置为另一个Responder[T]
。不需要阻止,只需保持计划未来的计算!
好的,compute
函数的签名现在必须更改为Responder[Result]
,那么这对递归调用有何影响?我们试试这个:
private def compute( input: Input ):Responder[Result] = {
if( pairs.size < SIZE_LIMIT ) {
future(computeSequential())
} else {
val (input1,input2) = input.split
for {
result1 <- compute(input1)
result2 <- compute(input2)
} yield merge(result1, result2)
}
}
现在您不再需要使用compute
将调用包裹到future(...)
,因为他们已经返回Responder
(Future
的超类)。
使用这种延续传递方式的一个结果是,您的顶级代码 - 无论最初调用compute
- 都不再阻止。如果它是从main()
调用的,并且这就是所有程序所做的,这将是一个问题,因为现在它只会产生一堆未来,然后立即关闭,完成了它被告知要做的所有事情。您需要做的是block
所有这些未来,但只有一次,在顶层,并且只在所有计算的结果,而不是任何中间的结果。
不幸的是,Responder
返回的compute()
事件不再像apply()
那样具有阻止Future
方法。我不确定为什么flatMapping Future
会生成通用Responder
而不是Future
;这似乎是一个API错误。但无论如何,你应该能够自己创造:
def claim[A](r:Responder[A]):A = {
import java.util.concurrent.ArrayBlockingQueue
import scala.actors.Actor.actor
val q = new ArrayBlockingQueue[A](1)
// uses of 'respond' need to be wrapped in an actor or future block
actor { r.respond(a => q.put(a)) }
return q.take
}
现在,您可以在main
方法中创建一个阻止调用来计算,如下所示:
val finalResult = claim(compute(input))