在Scala中逐行读取和处理文件

时间:2012-12-12 15:27:51

标签: scala collections concurrency parallel-processing

假设我需要将两个函数f: String => Ag: A => B应用于大型文本文件中的每一行,以最终创建B列表。

由于文件很大且fg很昂贵,我想让处理并发。我可以使用“并行集合”并执行io.Source.fromFile("data.txt").getLines.toList.par.map(l => g(f(l))之类的操作,但它不会同时执行读取文件fg

在此示例中实现并发的最佳方法是什么?

2 个答案:

答案 0 :(得分:12)

首先,重要提示:不要在.par上使用List,因为它需要复制所有数据(因为List只能按顺序读取)。相反,请使用类似Vector的内容,.par转换可以在不进行复制的情况下进行。

看起来你正在以错误的方式思考并行性。这是将要发生的事情:

如果您有这样的文件:

0
1
2
3
4
5
6
7
8
9

功能fg

def f(line: String) = {
  println("running f(%s)".format(line))
  line.toInt
}

def g(n: Int) = {
  println("running g(%d)".format(n))
  n + 1
}

然后你可以这样做:

io.Source.fromFile("data.txt").getLines.toIndexedSeq[String].par.map(l => g(f(l)))

获得输出:

running f(3)
running f(0)
running f(5)
running f(2)
running f(6)
running f(1)
running g(2)
running f(4)
running f(7)
running g(4)
running g(1)
running g(6)
running g(3)
running g(5)
running g(0)
running g(7)
running f(9)
running f(8)
running g(9)
running g(8)

因此即使整个g(f(l))操作发生在同一个线程上,您也可以看到每一行可以并行处理。因此,许多fg操作可以在不同的线程上同时发生,但特定行fg将在顺序。

毕竟,这是你应该期待的方式,因为它实际上无法读取该行,运行f并并行运行g。例如,如果尚未读取该行,它如何在g的输出上执行f

答案 1 :(得分:3)

您可以在map上使用Future

val futures = io.Source.fromFile(fileName).getLines.map{ s => Future{ stringToA(s) }.map{ aToB } }.toIndexedSeq

val results = futures.map{ Await.result(_, 10 seconds) }
// alternatively:
val results = Await.result(Future.sequence(futures), 10 seconds)