Scala 无法使用函数API执行并行计算

Scala 无法使用函数API执行并行计算,scala,functional-programming,Scala,Functional Programming,我试图实现并行性章节红皮书中的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]

我试图实现并行性章节红皮书中的
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的线程池),但是我只能看到打印的主线程

有什么建议吗?
谢谢

在提问时,我想从一条建议开始——你应该始终提供一条建议。你的代码没有编译;例如,我不知道
UnitFuture
来自何处,我不知道您正在使用的
sequence
的实现是什么,等等

下面是一个使用标准Scala的代码片段。首先,解释:

方法
countWords
获取要计数的字符串列表,以及两个服务—一个用于处理不同线程上的Java未来,另一个用于处理不同线程上的Scala未来。Scala one是通过
ExecutionContext.fromExecutor
方法从Java one派生而来的

为什么同时使用Java和Scala?嗯,我想保留Java,因为这是您最初编写代码的方式,但我不知道如何对Java的未来进行排序。所以我所做的是:

  • 对于每个子字符串:
    • fork是Java未来的任务
    • 把它变成Scala的未来
  • 对获得的Scala期货列表进行排序
如果您不熟悉隐式,您会(如果您打算使用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
线程:线程[pool-1-Thread-3,5,main],处理字符串:hg-ks
线程:线程[pool-1-Thread-2,5,main],处理字符串:lh-ks
线程:线程[pool-1-Thread-4,5,main],处理字符串:lh-hs
成功(8)

注意,由执行上下文决定线程。运行几次,您将自己看到-您可能最终只使用了两个线程:

线程:线程[pool-1-Thread-1,5,main],处理字符串:ab-cd
线程:线程[pool-1-Thread-3,5,main],处理字符串:hg-ks
线程:线程[pool-1-Thread-1,5,main],处理字符串:lh-ks
线程:线程[pool-1-Thread-1,5,main],处理字符串:lh-hs
成功(8)


这方面有什么进展吗?正如您在代码示例中所说的那样,
println
表达式不应该放在
countWords
函数中。通过在
fork
中的
call
方法中放置表达式,我可以看到此计算中涉及的所有线程。