Какие операции выполняются оптом при использовании параллельных коллекций? Странное поведение здесь - PullRequest
5 голосов
/ 18 сентября 2011

Введите следующую небольшую последовательную программу и ее распараллеленную версию в 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 с X.main(Array.empty) дает:

1
2
3
4
5
6
7
8
9
10
[total 10002ms]

Тогда как Y с Y.main(Array.empty) дает:

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] соответственно. Как это может быть?

Ответы [ 2 ]

7 голосов
/ 18 сентября 2011

Это не имеет ничего общего с параллельными коллекциями. Проблема скрыта в литерале функции. Вы можете увидеть это, если разрешите компилятору показывать 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 на

(1 to 10).foreach{x => Thread.sleep(1000);println(x)}

Но в чем разница? В вашем коде вы объявляете блок на foreach, и после выполнения блока он возвращает функцию для выполнения. Затем эта возвращенная функция доставляется в foreach, а не в блок, в котором она содержится.

Эта ошибка часто совершается. Это связано с литералом подчеркивания. Может быть этот вопрос вам поможет.

0 голосов
/ 03 января 2014

Интересный способ думать об этом состоит в том, что, поскольку scala является вызовом по значению ( Вызов по имени против вызова по значению в Scala, требуется пояснение ), когда вы вручаете {Thread.sleep (1000) ; println ()}, чтобы каждый раз вы оценивали блок {Thread.sleep (1000); println ()} только один раз и передавали только результирующую функцию println (_) для foreach. Когда вы выполняете foreach (x => Thread.sleep (1000); println (x)), вы передаете Thread.sleep (1000), а также println (x) в функцию foreach. Это просто еще один способ сказать то, что уже сказал sschaef.

...