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
的值进行初始化。
#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
的值进行初始化。