mersenne twister 线程对 cpp 安全吗

Is mersenne twister thread safe for cpp

#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()); 是多余的吗?

Can multiple threads call this function safely? Is the function thread safe?

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

当然,多个线程的输出可以交错,因为cout不同步。

Do I need to initialize the engine in every function call

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

or would it be better to put the first two lines (seeder and engine) in a class constructor?

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

但是跨线程使用同一个引擎确实不是线程安全的。您可以改为在每个线程中使用一个引擎 - 或者使用互斥体保护共享引擎,但这会产生很大的开销。

没有 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是没用的

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

这就完成了相对昂贵的 "real random number" 生成一次,通过引擎通过分发生成一长串伪随机数。

但是请注意,这样的使用是有成本的;您必须存储 engine 并且必须防止多线程访问它。

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

如果您不喜欢显式传递随机状态的想法,您可以使用 thread_local(或带有 mutex 守卫的 static)。

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

这会为每个线程创建一个 engine,并且 engine 使用来自您的 random_device 的值进行初始化。