给定以下通过scala.concurrent.Task创建的Task#delay实例
val t =
Task.delay { println(Thread.currentThread); Thread.sleep(5000); 42 }我编写了一个将异步运行t的方法。
def f = t.runAsync {
case \/-(x) => println(x)
case -\/(e) => println(e.getMessage)
}运行它显示f完全计算,即等待5秒,然后再计算。换句话说,第二个f似乎要等到第一个f完成。
scala> {f; f; }
Thread[run-main-0,5,run-main-group-0]
42
Thread[run-main-0,5,run-main-group-0]
42然后,我用t重写了Task#apply。
val u =
Task { println(Thread.currentThread); Thread.sleep(5000); 42 }再次,我定义了一个使用u执行runAsync的方法。
def g = u.runAsync {
case \/-(x) => println(x)
case -\/(e) => println(e.getMessage)
}最后,我运行了两个g's。
scala> {g; g}
Thread[pool-3-thread-2,5,run-main-group-0]
Thread[pool-3-thread-3,5,run-main-group-0]
scala> 42
42然而,在上述结果中,g或多或少是同时运行的。
我原以为{f; f; }会异步运行,即以与g相同的方式运行。但是,在我看来,打电话给f造成了麻烦。
编辑
任务在runAsync上的文档记录
任何纯的、非异步的计算都将在调用线程中强制执行。
由于t的主体是非异步的,我想上面的注释解释了为什么它会阻塞,即“在调用线程中强制执行”。
什么时候是使用Task#delay和Task#apply的合适时机?
发布于 2017-03-11 21:01:17
您可以将Task.delay视为类似于() => Try[A]的高级版本。它暂停计算的计算,但对于评估最终将在哪个线程上运行没有什么好说的(这意味着它只是在当前线程上运行)。
这往往正是你想要的。考虑一下这样的定义:
val currentTime: Task[Long] = Task.xxx(System.currentTimeMillis)我们不能使用now,因为这将立即评估时间(并且只在定义上只计算一次)。我们可以使用apply,但是强制这个计算的异步边界是浪费和不必要的-我们实际上希望它在当前线程中运行,而不是现在。这正是delay所提供的。
通常,当您对计算建模时,如果某些东西总是计算成本很高,您可能需要考虑Task.apply,这意味着计算总是在由当前隐式ExecutorService确定的线程上进行。这可能会使使用变得更干净,而以牺牲灵活性为代价--您正在将您所知道的有关计算计算的运行时特性的内容写入其定义中。
使用delay来定义异步计算的好处是,始终可以通过用Task.fork包装Task来强制异步边界,这在本质上和用Task.apply定义计算时得到的一样。不可能朝相反的方向发展--如果使用Task.apply,隐式策略将确定计算的计算位置,这就是它的全部内容。
https://stackoverflow.com/questions/42739896
复制相似问题