我参加了课程Parallel Programming,它显示了并行界面:
def parallel[A, B](taskA: => A, taskB: => B): (A, B) = {
val ta = taskA
val tb = task {taskB}
(ta, tb.join())
}
以下是错误的:
def parallel[A, B](taskA: => A, taskB: => B): (A, B) = {
val ta = taskB
val tb = task {taskB}.join()
(ta, tb)
}
在https://gist.github.com/ChenZhongPu/fe389d30626626294306264a148bd2aa
更多地查看界面它还向我们展示了执行四项任务的正确方法:
def parallel[A, B, C, D](taskA: => A, taskB: => B, taskC: => C, taskD: => D): (A, B, C, D) = {
val ta = task { taskA }
val tb = task { taskB }
val tc = task { taskC }
val td = taskD
(ta.join(), tb.join(), tc.join(), td)
}
我的问题:如果我不知道推进的任务数量(任务列表),我该如何正确地为每项任务打电话join
?
tasks.map(_.join()) // wrong
修改
答案 0 :(得分:2)
您可以实现以下方法:
def parallel[A](tasks: (() => A)*): Seq[A] = {
if (tasks.isEmpty) Nil
else {
val pendingTasks = tasks.tail.map(t => task { t() })
tasks.head() +: pendingTasks.map(_.join())
}
}
(请注意,您can't have variable number of by-name arguments - 尽管此can change)
然后像这样使用它:
object ParallelUsage {
def main(args: Array[String]) {
val start = System.currentTimeMillis()
// Use a list of tasks:
val tasks = List(longTask _, longTask _, longTask _, longTask _)
val results = parallel(tasks: _*)
println(results)
// or pass any number of individual tasks directly:
println(parallel(longTask, longTask, longTask))
println(parallel(longTask, longTask))
println(parallel(longTask))
println(parallel())
println(s"Done in ${ System.currentTimeMillis() - start } ms")
}
def longTask() = {
println("starting longTask execution")
Thread.sleep(1000)
42 + Math.random
}
}
你不能比这简单:
val tasks = Vector(longTask _, longTask _, longTask _)
val results = tasks.par.map(_()).seq
答案 1 :(得分:0)
受Future.sequence
启发并作弊。您需要一个Task
实施,这也是一个Monad,可以使这个设计工作。
/** Transforms a `TraversableOnce[Task[A]]` into a `Task[TraversableOnce[A]]`.
* Useful for reducing many `Task`s into a single `Task`.
*/
def parallel[
A,
M[X] <: TraversableOnce[X]
](in: M[Task[A]])(
implicit cbf: CanBuildFrom[M[Task[A]], A, M[A]],
executor: ExecutionContext
): Task[M[A]] = {
in.foldLeft(Task.point(cbf(in))) {
(fr, fa) => for (r <- fr; a <- fa) yield (r += a)
}.map(_.result())(executor)
}
这可以为大多数Scala集合并行执行操作,唯一的条件是Task
定义map
和flatMap
,无论哪个实现都是,因为您可以抽象特定的使用implicit builder
构造的集合类型,它是Scala库的内部构件。
答案 2 :(得分:0)
寻找构建parallel()
的实用方法,我发现它可以从Future
构建。任何使用现代Javascript Promises
的人都会熟悉这种范例:
import scala.concurrent.{Await,Future}
import scala.concurrent.duration.Duration
import scala.concurrent.ExecutionContext.Implicits.global
def parallel[A, B](taskA: =>A, taskB: =>B): (A,B) = {
val fB:Future[B] = Future { taskB }
val a:A = taskA
val b:B = Await.result(fB, Duration.Inf)
(a,b)
}
这会将taskB旋转到它自己的线程并在主线程中执行taskA。我们会taskA
并在必要时永远等待fB
完成。请注意,我没有使用此设置测试异常,它可能会停滞或行为异常。