Haskell中的随机数生成器是线程安全的吗?

时间:2011-08-22 20:32:13

标签: multithreading random thread-safety haskell

所有线程共享相同的“全局随机数生成器”,还是每个线程都有自己的?

如果共享一个,我如何确保线程安全?使用"Monads" chapter of Real World Haskell中描述的 getStdGen setStdGen 的方法看起来并不安全。

如果每个线程都有一个独立的发生器,两个线程的生成器会快速连续启动会有不同的种子吗? (例如,如果种子是以秒为单位的时间,它们将不会,但毫秒可能没​​问题。我不知道如何从Data.Time获得毫秒分辨率的时间。)

4 个答案:

答案 0 :(得分:14)

有一个名为newStdGen的函数,它给出了一个新的std。 gen每次被叫。 Its implementation使用atomicModifyIORef,因此是线程安全的。

newStdGen优于get / setStdGen,不仅在线程安全性方面,而且它还可以防止您遇到类似这样的潜在单线程错误:let rnd = (fst . randomR (1,5)) <$> getStdGen in (==) <$> rnd <*> rnd

此外,如果你考虑newStdGen vs getStdGen / setStdGen的语义,第一个可以非常简单:你只需要一个新标准。处于随机状态的gen,非确定性地选择。另一方面,使用get / set对,你无法抽象出全局程序状态,这有多种原因。

答案 1 :(得分:10)

我建议你只使用getStdGen一次(在主线程中),然后使用split函数生成新的生成器。我会这样做:

制作包含发电机的MVar。每当线程需要一个新的生成器时,它会从MVar中取出当前值,调用split并将新生成器放回原处。由于MVar的功能,这应该是线程安全的。

答案 2 :(得分:3)

在某种意义上,getStdGensetStdGen本身并不是线程安全的。假设两个线程都执行此操作:

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

线程有可能在另一个线程到达g <- getStdGen之前运行setStdGen行,因此它们都可以获得完全相同的生成器。 (我错了吗?)

如果他们都抓住相同版本的生成器,并在同一个函数中使用它们,它们将获得相同的“随机”结果。因此,在处理随机数生成和多线程时,您需要更加小心。有很多解决方案;我想到的是一个专用的随机数生成器线程,它产生一个随机数流,其他线程可以以线程安全的方式使用它。正如FUZxxl所说,将发电机放入MVar可能是最简单,最直接的解决方案。

当然,我建议您检查一下代码并确保必要在多个帖子中生成随机数。

答案 3 :(得分:2)

您可以在FUZxxl的答案中使用split。但是,无论何时调用forkIO,只需将分叉线程的IO操作关闭在其中一个生成的生成器上,而将另一个放在原始线程中,而不是使用MVar。这样每个线程都有自己的生成器。

正如Dan Burton所说,请检查您的代码,看看您是否真的需要多个线程中的RNG。