在C ++ 11中,有许多新的随机数生成器引擎和分发函数。它们是否安全?如果您在多个线程之间共享一个随机分布和引擎,它是否安全并且您是否仍会收到随机数?我正在寻找的场景就像,
void foo() {
std::mt19937_64 engine(static_cast<uint64_t> (system_clock::to_time_t(system_clock::now())));
std::uniform_real_distribution<double> zeroToOne(0.0, 1.0);
#pragma omp parallel for
for (int i = 0; i < 1000; i++) {
double a = zeroToOne(engine);
}
}
使用OpenMP或
void foo() {
std::mt19937_64 engine(static_cast<uint64_t> (system_clock::to_time_t(system_clock::now())));
std::uniform_real_distribution<double> zeroToOne(0.0, 1.0);
dispatch_apply(1000, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^(size_t i) {
double a = zeroToOne(engine);
});
}
使用libdispatch。
答案 0 :(得分:26)
C ++ 11标准库具有广泛的线程安全性。 PRNG对象上的线程安全保证与容器上的相同。更具体地说,由于PRNG类都是伪 - 随机,即它们基于确定的当前状态生成确定性序列,所以实际上没有空间在所包含的状态之外的任何东西上偷看或戳戳(这也是用户可见的。
就像容器需要锁定以使它们安全共享一样,您必须锁定PRNG对象。这会使它变得缓慢且不确定。每个线程一个对象会更好。
§17.6.5.9[res.on.data.races]:
1本节规定了实施应满足的要求 防止数据争用(1.10)。每个标准库函数都应该 除非另有说明,否则符合各项要实施可能 在下面指定的情况下防止数据争用。
2 C ++标准库函数不得直接或间接 访问对象(1.10)可由当前以外的线程访问 除非通过直接或间接访问对象 函数的论证,包括这个。
3 C ++标准库函数不得直接或间接 修改除当前线程以外的线程可访问的对象(1.10) 除非通过直接或间接访问对象 function的非const参数,包括这个。
4 [注意:这意味着,例如,实现不能使用a 用于内部目的的静态对象,因为它没有同步 即使在没有明确共享的程序中,也可能导致数据竞争 对象之间的对象。 -endnote]
5 C ++标准库函数不得间接访问对象 可通过其参数或其容器的元素访问 参数除了通过调用其规范所需的函数 在那些容器元素上。
6通过调用标准库获得的迭代器操作 容器或字符串成员函数可以访问底层 容器,但不得修改它。 [注意:特别是容器 使迭代器无效的操作与操作冲突 与该容器关联的迭代器。 - 结束说明]
7实现可以在线程之间共享自己的内部对象 如果对象对用户不可见并且受到数据保护 比赛。
8除非另有说明,否则C ++标准库函数应具有 如果那些只在当前线程内执行所有操作 操作具有对用户可见的效果(1.10)。
9 [注意:这允许实现并行化操作,如果 没有明显的副作用。 - 结束说明]
答案 1 :(得分:3)
标准(好N3242
)似乎没有提到随机数生成是免费的(除了rand
不是),所以它不是(除非我错过了一些东西)。除此之外,让它们成为线程之间没有任何意义,因为它会产生相对沉重的开销(至少与数字本身相比),而没有真正赢得任何东西。
此外,我并没有真正看到有一个共享随机数生成器的好处,而不是每个线程有一个,每个都被轻微地不同地初始化(例如,从另一个生成器的结果,或当前线程id)。毕竟你可能不依赖于生成一定顺序的发生器每次运行。因此,我会将您的代码重写为类似的内容(对于openmp
,没有关于libdispatch
的线索):
void foo() {
#pragma omp parallel
{
//just an example, not sure if that is a good way too seed the generation
//but the principle should be clear
std::mt19937_64 engine((omp_get_thread_num() + 1) * static_cast<uint64_t>(system_clock::to_time_t(system_clock::now())));
std::uniform_real_distribution<double> zeroToOne(0.0, 1.0);
#pragma omp for
for (int i = 0; i < 1000; i++) {
double a = zeroToOne(engine);
}
}
}
答案 2 :(得分:1)
documentation未提及线程安全性,因此我认为它们 不 线程安全。