在Scala REPL中输入以下小顺序程序及其并行化版本:
/* Activate time measurement in "App" class. Prints [total <X> ms] on exit. */
util.Properties.setProp("scala.time", "true")
/* Define sequential program version. */
object X extends App { for (x <- (1 to 10)) {Thread.sleep(1000);println(x)}}
/* Define parallel program version. Note '.par' selector on Range here. */
object Y extends App { for (y <- (1 to 10).par) {Thread.sleep(1000);println(y)}}
使用X.main(Array.empty)
执行X会给出:
1
2
3
4
5
6
7
8
9
10
[total 10002ms]
而Y.main(Array.empty)
的Y给出:
1
6
2
7
3
8
4
9
10
5
[total 5002ms]
到目前为止一切顺利。但是该程序的以下两种变体如何:
object X extends App {(1 to 10).foreach{Thread.sleep(1000);println(_)}}
object Y extends App {(1 to 10).par.foreach{Thread.sleep(1000);println(_)}}
分别给我 [total 1002ms]
和 [total 1002ms]
的运行时间。 这怎么可能?
答案 0 :(得分:7)
这与并行集合无关。问题隐藏在函数文字中。如果让编译器显示AST(带选项-Xprint:typer
):
for (x <- (1 to 10)) {Thread.sleep(1000);println(x)}
产生
scala.this.Predef.intWrapper(1).to(10).foreach[Unit](((x: Int) => {
java.this.lang.Thread.sleep(1000L);
scala.this.Predef.println(x)
}))
,而
(1 to 10).foreach{Thread.sleep(1000);println(_)}
产生
scala.this.Predef.intWrapper(1).to(10).foreach[Unit]({
java.this.lang.Thread.sleep(1000L);
((x$1: Int) => scala.this.Predef.println(x$1))
})
有一点不同。如果您想要预期的结果,则必须将foreach-expression更改为
(1 to 10).foreach{x => Thread.sleep(1000);println(x)}
但有什么区别?在您的代码中,您向foreach
声明一个块,并在执行该块后返回要执行的函数。然后,此返回的函数将传递给foreach
,而不是包含它的块。
这个错误经常发生。它与下划线文字有关。也许this question会帮助你。
答案 1 :(得分:0)
一种有趣的思考方式是,因为当您将{Thread.sleep(1000); println()}提交给foreach时,scala是按值调用(Call by name vs call by value in Scala, clarification needed)块{Thread.sleep(1000); println()}只有一次,只将生成的println(_)函数传递给foreach。当你执行foreach(x =&gt; Thread.sleep(1000); println(x))时,你将Thread.sleep(1000)以及println(x)交给函数foreach。这只是说sschaef已经说过的另一种方式。