C++11随机数生成器的线程安全性
C++11 Thread safety of Random number generators
在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。
C++11标准库是广泛的线程安全库。PRNG对象上的线程安全保证与容器上的相同。更具体地说,由于PRNG类都是伪随机的,即它们基于确定的当前状态生成确定性序列,因此实际上没有空间窥探或戳包含状态之外的任何东西(用户也可以看到)。
正如容器需要锁来确保它们可以安全共享一样,您也必须锁定PRNG对象。这会使它变得缓慢和不确定。每个线程一个对象会更好。
§17.6.5.9[种族数据]:
1本节规定了实施应满足的要求以防止数据竞赛(1.10)。每个标准库函数应除非另有规定,否则应满足各项要求。实施方式可能在以下指定情况之外的其他情况下防止数据争用。
2 C++标准库函数不得直接或间接可由当前线程以外的线程访问的访问对象(1.10)线程,除非通过函数的论点,包括这个。
3 C++标准库函数不得直接或间接修改当前线程以外的线程可访问的对象(1.10)线程,除非通过函数的非常量参数,包括这个。
4[注意:例如,这意味着实现不能使用用于内部目的而没有同步的静态对象,因为它即使在没有明确共享的程序中也可能导致数据竞争线程之间的对象--尾注]
5 C++标准库函数不应间接访问对象可通过其参数或容器的元素访问参数,除非调用其规范所需的函数在那些容器元素上。
6通过调用标准库获得的迭代器上的操作容器或字符串成员函数可以访问底层容器,但不得修改。[注:特别是容器使迭代器无效的操作与上的操作冲突与该容器关联的迭代器。——尾注]
7实现可以在线程之间共享它们自己的内部对象如果对象对用户不可见并且受到数据保护比赛。
8除非另有规定,C++标准库函数应仅在当前线程内执行所有操作,如果操作具有用户可见的效果(1.10)。
9[注意:如果没有明显的副作用。——尾注]
标准(好吧,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);
}
}
}
文档没有提到线程安全,所以我认为它们是而不是线程安全的。
- 类与私有变量的其他类之间的线程安全性
- 调用socket.remote_endpoint(提升 asio 库)线程安全性
- std::lock_guard 似乎提供了线程安全性,尽管作用域块
- C++中向量和列表的非写入成员函数的线程安全性
- 线程安全性和静态变量/成员功能
- 是仅使用get或toplown的原始类型的线程安全性的威胁
- 提高 ASIO stream_descriptor和事件 FD 线程安全性
- 是boost :: asio :: thread_pool线程在多个线程上发布任务时的安全性
- OpenMP中树结构的线程安全性
- 静态变量初始化的线程安全性
- c++线程的安全性和时间效率:为什么有互斥检查的线程有时比没有它的线程工作得更快
- 从另一个(非 qt)线程调用 QObject 方法的线程安全性?
- C 共享_ptr如何确保线程安全性
- 编写std::vector与普通数组的线程安全性
- C++标准库容器相对于所包含对象的线程安全性
- 读取和写入操作的线程安全性C++
- Qt库-静态成员函数的线程安全性
- QObject可重入性和线程安全性
- std::map中的线程安全性
- boost::asio io_service 和 std::containers 的线程安全性