Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/scala/16.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Scala并行集合foreach返回不同的结果_Scala_Parallel Processing_Functional Programming - Fatal编程技术网

Scala并行集合foreach返回不同的结果

Scala并行集合foreach返回不同的结果,scala,parallel-processing,functional-programming,Scala,Parallel Processing,Functional Programming,为什么在foreach函数中添加println语句会改变结果 var sum = 0 val list = (1 to 100).toList.par list.tasksupport = new ForkJoinTaskSupport(new scala.concurrent.forkjoin.ForkJoinPool(4)) list.foreach ((x: Int) => { println (x,sum); sum += x}) //5050 println (s

为什么在foreach函数中添加println语句会改变结果

var sum = 0
val list = (1 to 100).toList.par
 list.tasksupport = 
   new ForkJoinTaskSupport(new scala.concurrent.forkjoin.ForkJoinPool(4))
 list.foreach ((x: Int) => { println (x,sum); sum += x})
 //5050
 println (sum)
 sum = 0
 list.foreach ((x: Int) => sum += x)
 //results vary
 println (sum)

这是一个竞争条件,因为List是一个并行集合,foreach将并行运行,并对未同步的变量sum进行变异

现在为什么要在第一个foreach中打印正确的结果?由于块内部存在
println
,如果将其删除,您将遇到数据竞争

println委托给
PrintStream.println
,它内部有一个
synchronized

 public void println(Object x) {
    String s = String.valueOf(x);
    synchronized (this) {
        print(s);
        newLine();
    }
}

顺便说一句,这不是并行求和的好方法。

这是一个竞争条件,因为列表是一个并行集合,每个集合将并行运行,并对未同步的变量求和进行变异

现在为什么要在第一个foreach中打印正确的结果?由于块内部存在
println
,如果将其删除,您将遇到数据竞争

println委托给
PrintStream.println
,它内部有一个
synchronized

 public void println(Object x) {
    String s = String.valueOf(x);
    synchronized (this) {
        print(s);
        newLine();
    }
}

顺便说一句,这不是并行求和的好方法。

Scala鼓励不变性而不是易变性,特别是因为这样的事情会发生。当您有可以更改的
val
变量时,您可以通过更改内存中的值来创建竞争条件,这些值可能尚未被另一个未实现更改的线程读取

这样并行求和会导致以下情况: 要调用该函数的所有线程 *3个线程将值总和读取为0, *1线程写入
sum+x
,它恰好是
34
,因为它是并行的,加法可以按任意顺序进行 *又有一个线程写入
sum+x
,它将其计算为
0+17
(假设*为17),因为它在写入内存之前读取了值0 *2个线程读取17 *前三个线程中的最后一个线程写入
0+9
,因为它读取了0

TLDR中,对内存的读取和写入不同步,因为几个线程可能在其他线程写入时读取,并覆盖其他线程的更改

解决方案是找到一种按顺序进行的方法,或者以非破坏性的方式利用并行化。像sum这样的函数应该按顺序执行,或者以总是生成新值的方式执行,例如foldLeft:

Seq(1, 2, 3, 4).foldLeft(0){case (sum, newVal) => sum + newVal}
或者您可以编写一个函数,创建和的子集,并行相加,然后按顺序将所有这些相加:

Seq(1, 2, 3, 4, 5, 6, 7, 8).grouped(2).toSeq.par.map {
  pair =>
   pair.foldLeft(0){case (sum, newVal) => sum + newVal}
}.seq.foldLeft(0){case (sum, newVal) => sum + newVal}

Scala鼓励不变性而不是易变性,特别是因为这样的事情会发生。当您有可以更改的
val
变量时,您可以通过更改内存中的值来创建竞争条件,这些值可能尚未被另一个未实现更改的线程读取

这样并行求和会导致以下情况: 要调用该函数的所有线程 *3个线程将值总和读取为0, *1线程写入
sum+x
,它恰好是
34
,因为它是并行的,加法可以按任意顺序进行 *又有一个线程写入
sum+x
,它将其计算为
0+17
(假设*为17),因为它在写入内存之前读取了值0 *2个线程读取17 *前三个线程中的最后一个线程写入
0+9
,因为它读取了0

TLDR中,对内存的读取和写入不同步,因为几个线程可能在其他线程写入时读取,并覆盖其他线程的更改

解决方案是找到一种按顺序进行的方法,或者以非破坏性的方式利用并行化。像sum这样的函数应该按顺序执行,或者以总是生成新值的方式执行,例如foldLeft:

Seq(1, 2, 3, 4).foldLeft(0){case (sum, newVal) => sum + newVal}
或者您可以编写一个函数,创建和的子集,并行相加,然后按顺序将所有这些相加:

Seq(1, 2, 3, 4, 5, 6, 7, 8).grouped(2).toSeq.par.map {
  pair =>
   pair.foldLeft(0){case (sum, newVal) => sum + newVal}
}.seq.foldLeft(0){case (sum, newVal) => sum + newVal}

谢谢你的回复。有没有办法像第二个foreach那样跟踪变异(跟踪变量sum在哪个线程中更新的时间)?这是我最初的目标。谢谢你的回复。有没有办法像第二个foreach那样跟踪变异(跟踪变量sum在哪个线程中更新的时间)?这是我最初的目标。