#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++
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
中的值进行初始化。
多线程可以安全地调用这个函数吗?该函数线程安全吗?
这个特定的函数是线程安全的。可以创建随机数生成器、引擎和分布,并在多个线程中的函数本地引擎中调用生成数字。
当然,多个线程的输出可以交错,因为
cout
不同步。
我需要在每个函数调用中初始化引擎吗
这就是你的函数所做的,虽然这确实保证了线程安全,但它与你需要做的相反。每次初始化引擎都会使“随机性”序列直接取决于播种器。当然,它会增加初始化引擎的开销。
或者将前两行(播种器和引擎)放在类构造函数中会更好吗?
您可以使用包装类,但不是必须的。这与您是否在每个函数调用中创建新的引擎实例正交。只要每个函数调用使用与之前调用相同的引擎,就没有这方面的随机性问题。
但是跨线程使用相同的引擎确实不是线程安全的。您可以在每个线程中使用一个引擎,例如通过
thread_local
- 或者使用互斥体保护共享引擎,但这有其自身的开销成本以及任何阻塞时间。