mersennetwister 线程对 cpp 安全吗

问题描述 投票:0回答:2
#include <random>

int f() {

    std::random_device seeder;
    std::mt19937 engine(seeder());
    std::uniform_int_distribution<int> dist(1, 6);

    return dist(engine);

}

多线程可以安全地调用这个函数吗?该函数是线程安全的吗? 每次都打电话

std::random_device seeder;
std::mt19937 engine(seeder());
是多余的吗?

c++ multithreading c++11 mersenne-twister
2个回答
17
投票

没有 C++

std
类型以非线程安全的方式使用全局数据。可以在不同的线程中访问此类类型的两个不相关的实例。

默认情况下,如果没有同步,则无法从两个线程访问一种类型的一个实例。

您已创建局部变量。这些局部变量与其类型的任何其他实例无关。这里不存在线程安全问题。

通过拥有状态并重用它,可以最有效地生成伪随机值。您没有这样做,因此创建从 1 到 6 的随机数将相对昂贵。

std::random_device seeder;
std::mt19937 engine(seeder());
std::uniform_int_distribution<int> dist(1, 6);
return dist(engine);

您使用

std::mt19937
是多余的。您已经在创建一个
random_device
,它可以直接输入到
dist
,然后从中创建一个
engine
,然后使用
engine
。这里使用
engine
是没有用的。

传统上,您从

engine
创建
mt19937
(某种类型,如 seeder
once
。然后存储
engine
,并重复将其传递给发行版。

这会执行相对昂贵的“真实随机数”生成一次,通过引擎通过分发生成一长串伪随机数。

但请注意,这种使用是有成本的;您必须存储

engine
并且必须防止多线程访问它。

“正确”的方法是让一个对象为您生成随机值,并将其传递到您需要的地方。存储使用的初始种子还允许您重复执行所涉及的随机数集。

如果您不喜欢显式传递随机状态的想法,您可以使用

thread_local
(或带有
static
保护的
mutex
)。

thread_local std::mt19937 engine(std::random_device{}());
std::uniform_int_distribution<int> dist(1, 6);
return dist(engine);

这将为每个线程创建一个

engine
,并且
engine
使用
random_device
中的值进行初始化。


4
投票

多线程可以安全地调用这个函数吗?该函数线程安全吗?

这个特定的函数是线程安全的。可以创建随机数生成器、引擎和分布,并在多个线程中的函数本地引擎中调用生成数字。

当然,多个线程的输出可以交错,因为

cout
不同步。

我需要在每个函数调用中初始化引擎吗

这就是你的函数所做的,虽然这确实保证了线程安全,但它与你需要做的相反。每次初始化引擎都会使“随机性”序列直接取决于播种器。当然,它会增加初始化引擎的开销。

或者将前两行(播种器和引擎)放在类构造函数中会更好吗?

您可以使用包装类,但不是必须的。这与您是否在每个函数调用中创建新的引擎实例正交。只要每个函数调用使用与之前调用相同的引擎,就没有这方面的随机性问题。

但是跨线程使用相同的引擎确实不是线程安全的。您可以在每个线程中使用一个引擎,例如通过

thread_local
- 或者使用互斥体保护共享引擎,但这有其自身的开销成本以及任何阻塞时间。

© www.soinside.com 2019 - 2024. All rights reserved.