std::random_device的线程安全性

24
我有一些代码,看起来有点像这样:

std::random_device rd;

#pragma omp parallel
{
    std::mt19937 gen(rd());
    #pragma omp for
    for(int i=0; i < N; i++)
    {
        /* Do stuff with random numbers from gen() */
    }
}

我有几个问题:

  • std::random_device是否线程安全?也就是说,如果多个线程同时调用它,它会做一些无用的事情吗?
  • 这通常是一个好主意吗?我应该担心重叠的随机数流吗?
  • 有没有更好的方法来实现我想要的(每个线程独立的随机数流 - 目前我不太担心可重复性)?

如果对std::random_device的工作方式有任何影响,我主要在Windows上运行,但我希望代码在Linux和OSX上同样有效。


你可以通过使用特定的种子而不是使用std::random_device来实现可重复性。 - Galik
random_device 很可能会阻塞。如果你想要并行性,那么这样使用它就没有太多意义了。你可以使用一个全局 PRNG 来种子化 mt19937,种子值来自于 random_device(但是需要显式锁定)。 - sbabbi
另请参阅 https://dev59.com/gWEi5IYBdhLWcg3wfsQj#21238187 - Mikhail
2个回答

6

在并行中使用随机设备不是一个好主意。即使它是阻塞的,你可能不会遇到重叠的随机数流的问题,但你会增加一个额外的同步点。

你应该设置尽可能多的随机数引擎(RNE),每个线程都要有一个,omp_get_num_threads()。创建一个 RNEs 的 std::vector,并在程序的顺序部分进行种子处理。对于种子处理,可以使用随机设备和 std::seed_seq

然后在每个线程中使用与线程编号关联的 RNE,omp_get_thread_num()

永远不要使用随机设备生成随机数,它很慢,通常不能生成均匀分布的随机数!

根据您需要的随机数质量,可以使用预定义的随机数生成器之一。如果您正在进行蒙特卡罗模拟或加密,请特别小心选择算法。

您可以在https://en.cppreference.com/w/cpp/numeric/random上找到大量有用的随机数引擎信息。


你可能想要使用 omp_get_max_threads() 实际查询线程数。当并行上下文用完时,omp_get_num_threads() 将始终返回 1。 - val is still with Monica

1
在没有WinRT的Windows系统中,它使用线程安全的 CryptGenRandom,详情参考https://stackoverflow.com/a/46171432/2024042
在有WinRT的Windows系统中,它使用 CryptographicBuffer::GenerateRandom。目前没有关于其线程安全性的文档,但它似乎没有状态,因此应该是线程安全的。
在Linux中,它似乎从/dev/urandom读取,这是线程安全的。
我从libs/random/src/random_device.cpp中阅读了这个实现。
我不知道文件中的_CXXRT_STD_NAME是什么,谷歌搜索只会出现boost::random_device。也许这没什么!

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接