简体   繁体   English

对期货做出反应

[英]React for futures

I am trying to use a divide-and-conquer (aka fork/join) approach for a number crunching problem. 我试图使用分而治之(又称fork / join)方法来解决数字运算问题。 Here is the code: 这是代码:

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)
  }
}

It runs (with a nice speed-up) but the the future apply method seems to block a thread and the thread pool increases tremendously. 它运行(具有良好的加速)但未来的apply方法似乎阻塞了一个线程,并且线程池大大增加。 And when too many threads are created, the computations is stucked. 并且当创建太多线程时,计算被卡住。

Is there a kind of react method for futures which releases the thread ? 是否存在一种释放线程的期货反应方法? Or any other way to achieve that behavior ? 或任何其他方式来实现这种行为?

EDIT: I am using scala 2.8.0.final 编辑:我使用scala 2.8.0.final

Don't claim (apply) your Future s, since this forces them to block and wait for an answer; 不要求(申请)你的Future ,因为这迫使他们阻止并等待答案; as you've seen this can lead to deadlocks. 正如你所见,这可能会导致死锁。 Instead, use them monadically to tell them what to do when they complete. 相反,单独使用它们告诉他们完成后该怎么做。 Instead of: 代替:

val result1 = f1()
val result2 = f2()
merge(result1,result2)

Try this: 尝试这个:

for {
  result1 <- f1
  result2 <- f2
} yield merge(result1, result2)

The result of this will be a Responder[Result] (essentially a Future[Result] ) containing the merged results; 结果将是包含合并结果的Responder[Result] (基本上是Future[Result] ); you can do something effectful with this final value using respond() or foreach() , or you can map() or flatMap() it to another Responder[T] . 你可以使用response respond()foreach() respond()这个最终值做一些有效的事情,或者你可以将map()flatMap() map()到另一个Responder[T] No blocking necessary, just keep scheduling computations for the future! 不需要阻止,只需保持计划未来的计算!

Edit 1: 编辑1:

Ok, the signature of the compute function is going to have to change to Responder[Result] now, so how does that affect the recursive calls? 好的, compute功能的签名现在必须更改为Responder[Result] ,那么这对递归调用有何影响? Let's try this: 我们试试这个:

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)
  }
}

Now you no longer need to wrap the calls to compute with future(...) because they're already returning Responder (a superclass of Future ). 现在你不再需要用future(...)包含对compute的调用,因为它们已经返回ResponderFuture的超类)。

Edit 2: 编辑2:

One upshot of using this continuation-passing style is that your top-level code--whatever calls compute originally--doesn't block at all any more. 使用这种延续传递方式的一个结果是,您的顶级代码 - 无论是最初的compute调用 - 都不再阻止。 If it's being called from main() , and that's all the program does, this will be a problem, because now it will just spawn a bunch of futures and then immediately shut down, having finished everything it was told to do. 如果它是从main()调用的,并且这是所有程序所做的,这将是一个问题,因为现在它只会产生一堆未来,然后立即关闭,完成它被告知要做的所有事情。 What you need to do is block on all these futures, but only once, at the top level, and only on the results of all the computations, not any intermediate ones. 您需要做的是block所有这些未来,但只在顶级,只在所有计算的结果,而不是任何中间的结果。

Unfortunately, this Responder thing that's being returned by compute() no longer has a blocking apply() method like the Future did. 不幸的是,这个被compute()返回的Responder事件不再像Future那样有一个阻塞的apply()方法。 I'm not sure why flatMapping Future s produces a generic Responder instead of a Future ; 我不确定为什么flatMapping Future会生成一个通用的Responder而不是Future this seems like an API mistake. 这似乎是一个API错误。 But in any case, you should be able to make your own: 但无论如何,你应该能够自己创造:

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
}

So now you can create a blocking call to compute in your main method like so: 所以现在你可以在main方法中创建一个阻塞调用来计算,如下所示:

val finalResult = claim(compute(input))

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM