我正在尝试实现关于并行性一章的红皮书中的countWords
函数。当我将线程池传递给函数并修改函数以打印计数单词的线程时,我只能看到打印的主线程。这表明我无法使该函数并行执行。
我目前拥有的东西:
type Par[A] = ExecutorService => Future[A]
def asyncF[A, B](f: A => B): A => Par[B] = a => lazyUnit(f(a))
def lazyUnit[A](a: => A): Par[A] = fork(unit(a))
def unit[A](a: A): Par[A] = (_: ExecutorService) => UnitFuture(a)
def fork[A](a: => Par[A]): Par[A] =
es => es.submit(new Callable[A] {
def call = a(es).get
})
def countWords(l: List[String]): Par[Int] = map(sequence(l.map(asyncF {
println(Thread.currentThread())
s => s.split(" ").length
})))(_.sum)
我跑步时:
val listPar = List("ab cd", "hg ks", "lh ks", "lh hs")
val es = Executors.newFixedThreadPool(4)
val counts = countWords(listPar)(es)
println(counts.get(100, SECONDS))
我得到:
Thread[main,5,main]
8
我希望看到列表的每个元素都打印有一个线程(因为有四个元素和一个大小为4的线程池),但是我只能看到主线程被打印。
有什么建议吗? 谢谢
答案 0 :(得分:1)
在提出问题时,我想从一条建议开始-您应该始终提供MCVE。您的代码无法编译;例如,我不知道UnitFuture
的来源,也不知道您使用的sequence
的实现是什么。
这是与标准Scala一起使用的代码段。首先,说明:
方法countWords
包含一个要计数的字符串列表,以及两个服务-一个用于在不同线程上处理Java Futures,另一个用于在不同线程上处理Scala Futures。 Scala one是通过ExecutionContext.fromExecutor
方法从Java one派生的。
为什么同时使用Java和Scala?好吧,我想保留Java,因为那是您最初编写代码的方式,但是我不知道如何sequence
Java Future。所以我所做的是:
如果您不熟悉隐式,则可以(如果您打算使用Scala的话)。在这里,我隐式使用了执行上下文,因为它删除了很多样板-这样,在转换为Scala future时,映射/排序等过程中,我不必显式传递它。
现在代码本身:
import java.util.concurrent.{Callable, ExecutorService, Executors}
import java.util.concurrent.{Future => JFuture}
import scala.concurrent.{ExecutionContext, Future}
def scalaFromJavaFuture[A](
javaFuture: JFuture[A]
)(implicit ec: ExecutionContext): Future[A] =
Future { javaFuture.get }(ec)
def fork(s: String)(es: ExecutorService): java.util.concurrent.Future[Int] =
es.submit(new Callable[Int] {
def call = {
println(s"Thread: ${Thread.currentThread()}, processing string: $s")
s.split(" ").size
}
})
def countWords(l: List[String])(es: ExecutorService)(implicit ec: ExecutionContext): Future[Int] = {
val listOfFutures = l.map(elem => scalaFromJavaFuture(fork(elem)(es)))
Future.sequence(listOfFutures).map(_.sum)
}
val listPar = List("ab cd", "hg ks", "lh ks", "lh hs")
val es = Executors.newFixedThreadPool(4)
implicit val ec = ExecutionContext.fromExecutor(es)
val counts = countWords(listPar)(es)
counts.onComplete(println)
示例输出:
线程:线程[pool-1-thread-1,5,main],正在处理字符串:ab cd
线程:Thread [pool-1-thread-3,5,main],处理字符串:hg ks
线程:线程[pool-1-thread-2,5,main],处理字符串:lh ks
线程:Thread [pool-1-thread-4,5,main],处理字符串:lh hs
成功(8)
请注意,取决于执行上下文来确定线程。运行几次,您将自己看到-您可能会遇到例如仅使用了两个线程:
线程:线程[pool-1-thread-1,5,main],正在处理字符串:ab cd
线程:Thread [pool-1-thread-3,5,main],处理字符串:hg ks
线程:Thread [pool-1-thread-1,5,main],处理字符串:lh ks
线程:Thread [pool-1-thread-1,5,main],处理字符串:lh hs
成功(8)