Всякий раз, когда вы создаете искру для вычисления (что и делает parList
), всегда существует вероятность того, что работа для этого вычисления будет выполнена дважды. На практике это случается редко. В основном существует состояние гонки между потоками, обрабатывающими искры, и основным потоком.
Haskell реализует ленивость, изначально устанавливая значение переменной в thunk — по сути, указатель на код для вычисления значения. Когда требуется значение переменной, Haskell запускает код, на который указывает преобразователь, и заменяет преобразователь возвращаемым значением. Если переменная используется позже, Haskell просто использует сохраненное значение.
Когда вы оцениваете переменную параллельно, создается искра, указывающая на переменную. Когда искра обрабатывается фоновым потоком, она просто требует значение, на которое указывает искра. Если искра указывает на преобразователь, преобразователь запускается и обновляется возвращенным значением. Если искра указывает на уже вычисленное значение, ничего не происходит, и мы говорим, что искра затухла.
Таким образом, если вы оцениваете список, подобный [x,x,x,x,x,x]
, параллельно, для каждого элемента списка будет создана одна искра, и возможно, что две или более таких искр будут выполняться одновременно. Также возможно, что основной поток будет одновременно оценивать x
. В этом случае работа по вычислению x
будет дублироваться. Однако, как только преобразователь для x
был обновлен, никакие искровые или основные оценки потока x
, начинающиеся после этого, не будут пересчитывать x
.
24.07.2016
x
, но не возвращает. Тем временем параллельно, но после этого расчета, второй искровой доступx
. Эта искра получает значениеx
из расчета первой искры или она сама вычисляетx
? 25.07.2016x
, аx
в настоящее время является преобразователем, он выполнит работу по вычислениюx
, обновитx
вычисленным значением (так чтоx
больше не является преобразователем) и продолжит работу. 25.07.2016