首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >静态随机数发生器的线程安全

静态随机数发生器的线程安全
EN

Stack Overflow用户
提问于 2022-05-03 11:11:58
回答 2查看 471关注 0票数 5

我有很多线程,每个线程都需要一个线程安全的随机数。由于在我的实际程序中线程是反复生成和连接的,所以每次输入一个调用相同函数的新并行区域时,我都不想创建random_devicemt19937,所以我将它们作为静态的:

代码语言:javascript
运行
复制
#include <iostream>
#include <random>
#include <omp.h>

void test(void) {
    static std::random_device rd;
    static std::mt19937 rng(rd());
    static std::uniform_int_distribution<int> uni(1, 1000);

    int x = uni(rng);
#   pragma omp critical
    std::cout << "thread " << omp_get_thread_num() << " | x = " << x << std::endl;
}

int main() {
#   pragma omp parallel num_threads(4)
    test();
}

我不能将它们作为threadprivate,因为错误C3057:当前不支持“线程私有”符号的动态初始化。一些消息来源说random_devicemt19937是线程安全的,但是我还没有找到任何能证明这一点的文档。

  1. 这个随机化线程安全吗?
  2. 如果不是,哪些静态对象可以保留为静态对象以保持线程安全?
EN

回答 2

Stack Overflow用户

发布于 2022-05-03 14:56:17

这里有一种不同的方法。我保留了一个全局种子值,以便只使用random_device一次。由于使用它的速度可能很慢,我认为谨慎的做法是尽量少使用它。

相反,我们增加了每个线程的种子值,也增加了每次使用的种子值。这样我们就避免了生辰悖论,并将线程-局部状态最小化为一个整数。

代码语言:javascript
运行
复制
#include <omp.h>

#include <algorithm>
#include <array>
#include <random>


using seed_type = std::array<std::mt19937::result_type, std::mt19937::state_size>;


namespace {

  seed_type init_seed()
  {
    seed_type rtrn;
    std::random_device rdev;
    std::generate(rtrn.begin(), rtrn.end(), std::ref(rdev));
    return rtrn;
  }
  
}
/**
 * Provides a process-global random seeding value
 *
 * Thread-safe (assuming the C++ compiler if standard-conforming.
 * Seed is initialized on first call
 */
seed_type global_seed()
{
  static seed_type rtrn = init_seed();
  return rtrn;
}
/**
 * Creates a new random number generator
 *
 * Operation is thread-safe, Each thread will get its own RNG with a different
 * seed. Repeated calls within a thread will create different RNGs, too.
 */
std::mt19937 make_rng()
{
  static std::mt19937::result_type sequence_number = 0;
# pragma omp threadprivate(sequence_number)
  seed_type seed = global_seed();
  static_assert(seed.size() >= 3);
  seed[0] += sequence_number++;
  seed[1] += static_cast<std::mt19937::result_type>(omp_get_thread_num());
  seed[2] += static_cast<std::mt19937::result_type>(omp_get_level());
  std::seed_seq sseq(seed.begin(), seed.end());
  return std::mt19937(sseq);
}

还请参见:How to make this code thread safe with openMP? Monte Carlo two-dimensional integration

有关只增加种子值的方法,请参见以下内容:https://www.johndcook.com/blog/2016/01/29/random-number-generator-seed-mistakes/

票数 1
EN

Stack Overflow用户

发布于 2022-05-03 14:05:01

我认为threadprivate仍然是正确的方法,您可以通过稍后执行并行分配来避免初始化问题。

代码语言:javascript
运行
复制
static random_device rd;
static mt19937 rng;
#pragma omp threadprivate(rd)
#pragma omp threadprivate(rng)

int main() {

#pragma omp parallel
  rng = mt19937(rd());

#pragma omp parallel
  {
    stringstream res;
    uniform_int_distribution<int> uni(1, 100);
    res << "Thread " << omp_get_thread_num() << ": " << uni(rng) << "\n";
    cout << res.str();
  }

  return 0;
}

顺便说一下,注意stringstream:OpenMP倾向于在<<操作符上拆分输出行。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/72098550

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档