Scala Parallel Collections-如何提前返回?

时间:2011-12-11 22:15:14

标签: scala parallel-processing scala-2.9

我有一个可能的输入值列表

val inputValues = List(1,2,3,4,5)

我有一个很长的计算功能,给我一个结果

def reallyLongFunction( input: Int ) : Option[String] = { ..... }

使用scala并行集合,我可以轻松完成

inputValues.par.map( reallyLongFunction( _ ) )

同时得到所有结果。问题是,我真的不想要所有的结果,我只想要FIRST的结果。一旦我的一个输入成功,我想要我的输出,并希望继续我的生活。这做了很多额外的工作。

那么我如何才能充分利用这两个世界呢?我想

  1. 获取第一个从我的长函数返回内容的结果
  2. 停止我所有其他线程的无用工作。
  3. 修改 我通过

    解决了它像一个愚蠢的java程序员
    @volatile var done = false;
    

    reallyLongFunction内设置并检查了哪个。这有效,但感觉不太scala。想要一个更好的方法来做到这一点......

3 个答案:

答案 0 :(得分:4)

(更新:不,它不起作用,不做地图)

是否可以执行以下操作:

inputValues.par.find({ v => reallyLongFunction(v); true })

实现使用:

  protected[this] class Find[U >: T](pred: T => Boolean, protected[this] val pit: IterableSplitter[T]) extends Accessor[Option[U], Find[U]] {
    @volatile var result: Option[U] = None
    def leaf(prev: Option[Option[U]]) = { if (!pit.isAborted) result = pit.find(pred); if (result != None) pit.abort }
    protected[this] def newSubtask(p: IterableSplitter[T]) = new Find(pred, p)
    override def merge(that: Find[U]) = if (this.result == None) result = that.result
  }

看起来与你的@volatile非常相似,除非你不必看它; - )

答案 1 :(得分:3)

我以与huynhjl相同的方式解释了你的问题,但如果你只是想搜索并丢弃None,你可以做这样的事情,以避免在合适的结果是重复计算时实测值:

class Computation[A,B](value: A, function: A => B) {
  lazy val result = function(value)
}

def f(x: Int) = {          // your function here
  Thread.sleep(100 - x)
  if (x > 5) Some(x * 10)
  else None
}

val list = List.range(1, 20) map (i => new Computation(i, f))  
val found = list.par find (_.result.isDefined) 
  //found is Option[Computation[Int,Option[Int]]]
val result = found map (_.result.get)
  //result is Option[Int]

但并行集合的find似乎做了很多不必要的工作(参见this question),所以这可能效果不好,至少使用当前版本的Scala。

并行集合中使用了易失性标记(请查看findexistsforall的来源),因此我认为您的想法很好。如果你可以在函数本身中包含标志,那实际上会更好。它会杀死你的函数的引用透明度(即对于某些输入,你的函数现在有时会返回None而不是Some),但是由于你丢弃了已停止的计算,这应该不重要。

答案 2 :(得分:2)

如果你愿意使用非核心库,我认为期货将是这项任务的一个很好的匹配。例如:

...两者似乎都能启用您正在寻找的功能。