Является ли генератор случайных чисел в Haskell поточно-ориентированным? - PullRequest
15 голосов
/ 23 августа 2011

Является ли один и тот же "генератор глобальных случайных чисел" общим для всех потоков, или каждый поток получает свой собственный?

Если кто-то предоставил общий доступ, как я могу обеспечить безопасность потоков? Подход, использующий getStdGen и setStdGen , описанный в главе "Монады" в реальном мире Haskell , не выглядит безопасным.

Если каждый поток имеет независимый генератор, будут ли генераторы для двух потоков, запущенных в быстрой последовательности, иметь разные начальные числа? (Они не будут, например, если начальное время будет в секундах, но миллисекунды могут быть в порядке. Я не вижу, как получить время с миллисекундным разрешением из Data.Time.)

Ответы [ 4 ]

14 голосов
/ 23 августа 2011

Существует функция с именем newStdGen, которая дает новый стандарт.ген каждый раз, когда это называется. Его реализация использует atomicModifyIORef и поэтому является поточно-ориентированным.

newStdGen лучше, чем get / setStdGen, не только с точки зрения безопасности потоков, но и защищает вас от потенциальных однопоточных ошибок, таких как:Если вы думаете о семантике newStdGen против getStdGen / setStdGen, первые могут быть очень простыми: вы просто получаете новый стандарт.ген в случайном состоянии, выбран недетерминированно.С другой стороны, с парой get / set вы не можете абстрагироваться от глобального состояния программы, что плохо по нескольким причинам.

10 голосов
/ 23 августа 2011

Я бы предложил вам использовать getStdGen только один раз (в основном потоке), а затем использовать функцию split для генерации новых генераторов.Я бы сделал это так:

Создайте MVar, который содержит генератор.Всякий раз, когда потоку нужен новый генератор, он берет текущее значение из MVar, вызывает split и возвращает новый генератор обратно.Из-за функциональности MVar это должно быть поточно-ориентированным.

3 голосов
/ 23 августа 2011

Сами по себе getStdGen и setStdGen в определенном смысле не являются потокобезопасными.Предположим, что оба потока выполняют это действие:

do ...
   g <- getStdGen
   (v, g') <- someRandOperation g
   setStdGen g'

Оба потока могут запустить строку g <- getStdGen до того, как другой поток достигнет setStdGen, поэтому они оба могут получить один и тот же генератор,(Я не прав?)

Если они оба получат одну и ту же версию генератора и используют ее в одной и той же функции, они получат одинаковый «случайный» результат.Так что вам нужно быть немного более осторожным при работе со случайными числами и многопоточностью.Есть много решений;одно, что приходит на ум, - это иметь отдельный выделенный поток генератора случайных чисел, который генерирует поток случайных чисел, который другие потоки могли бы использовать потокобезопасным способом.Помещение генератора в MVar, как предполагает FUZxxl, является, вероятно, самым простым и простым решением.

Конечно, я бы посоветовал вам проверить ваш код и убедиться, что необходимо для генерациислучайные числа в более чем одном потоке.

2 голосов
/ 23 августа 2011

Вы можете использовать split как в ответе FUZxxl.Однако вместо использования MVar всякий раз, когда вы вызываете forkIO, просто закройте действие IO для разветвленного потока над одним из полученных генераторов и оставьте другой с исходным потоком.Таким образом, каждый поток имеет свой собственный генератор.

Как сказал Дэн Бертон, проверьте свой код и посмотрите, действительно ли вам нужен ГСЧ в нескольких потоках.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...