我试图了解 scalaz 并发包背后的想法和目的,主要是 Future 和 Task 类,但是在某些应用程序中使用它们时,它现在远非简单的顺序模拟,而scala.concurrent.Future
, 工作得更好。任何人都可以分享他用scalaz编写并发/异步应用程序的经验,基本上如何async
正确使用它的方法?正如我从消息来源了解到的那样,async
它不使用单独的线程,例如对 standard 的调用future
,或者fork/apply
来自 scalaz 的方法,那么为什么要调用它async
呢?这是否意味着为了与 scalaz 获得真正的并发性,我总是必须调用fork(now(...))
or apply
?
4 回答
我不是 scalaz 专家,但我会尽力帮助你。让我试着一一回答你的问题:
1)任何人都可以分享他用scalaz编写并发/异步应用程序的经验,基本上如何正确使用它的异步方法?
我们先来看看async
签名:
def async[A](listen: (A => Unit) => Unit): Future[A]
起初这可能有点神秘,因此最好查看测试以了解可能的用例。在https://github.com/scalaz/scalaz/blob/scalaz-seven/tests/src/test/scala/scalaz/concurrent/FutureTest.scala 中,您可以找到以下代码:
"when constructed from Future.async" ! prop{(n: Int) =>
def callback(call: Int => Unit): Unit = call(n)
Future.async(callback).run must_==
}
正如我们从签名中知道的那样,Future.async
只需使用签名函数构造新的 Future (A => Unit) => Unit
。这真正意味着 Future.async 将作为参数函数,对于给定的回调进行所有必需的计算并将结果传递给该回调。
重要的是要注意它Future.async
本身不运行任何计算,它只准备结构以便稍后运行它们。
2)据我了解,异步不使用单独的线程,例如对标准未来的调用,或者来自 scalaz 的 fork/apply 方法有效,那么为什么将其称为异步呢?
你是对的。只有fork
并且apply
似乎正在使用线程运行任何东西,这很容易注意到查看包含implicit pool: ExecutorService
. 我不能在这里代表作者发言,但我猜异步与回调有关。这意味着您将使用异步回调,而不是阻塞 Future 以获得最终结果。
3)这是否意味着为了与 scalaz 获得真正的并发性,我总是必须调用 fork(now(...)) 或应用?
据我所知,是的。请注意,当您使用语法创建 Future 时,您在这里Future(x)
使用的是apply
方法,所以这是一种默认行为(这很好)。
如果您想更好地了解 Scalaz Futures 的设计,我建议您阅读“Scala 中的函数式编程”。我相信这本书是由主要的 Scalaz 贡献者编写的,第 7 章讨论了为纯函数并行库设计 API。它与 Scalaz Future 并不完全相同,但您可以看到许多相似之处。
您还可以阅读关于 Scalaz 任务和未来的精彩的 Timothy Perrett博客文章,其中涵盖了许多不那么明显的细节。
async
用于将基于回调的异步 API 改编为Future
. 之所以调用它,是async
因为预计它将与异步运行的东西一起使用,可能会从另一个线程中调用回调。这是“真正的”并发,前提是您调用的 API 确实异步使用它(例如,我使用Future.async
AWS 开发工具包的异步部分,例如AmazonSimpleDBAsyncClient
)。
如果你想Task
直接从 scalaz API 获得“真正的”并发,你需要使用类似fork
or之类gatherUnordered
的东西,因为许多 API 默认是安全/确定性和可重新启动的,只有在明确请求时才具有并发性。
使用 map 和 flatMap 组合任务时,您可以通过不使用 fork 来获得性能优势,请参阅:
http://blog.higher-order.com/blog/2015/06/18/easy-performance-wins-with-scalaz/