明显 f += ... 无论右手边是一个竞争条件,但我想你已经知道了。
f += ...
我看到的主要问题是你对全局的使用 std::vector<std::mt19937> RNDS 。您的受互斥锁保护的关键部分仅包含添加新元素;不访问现有元素:
std::vector<std::mt19937> RNDS
... uniformRnd(RNDS[index]);
这不是线程安全的,因为调整大小 RNDS 在另一个线程可能会导致 RNDS[index] 被移动到新的内存位置。事实上,这可能发生在参考之后 RNDS[index] 计算但之前计算 uniformRnd 开始使用它,在这种情况下是什么 uniformRnd 认为是一个 Generator& 将是一个悬空指针,可能是一个新创建的对象。在任何情况下, uniformRnd 的 operator() 不保证数据竞赛[注1],也不保证 RNDS 的 operator[] 。
RNDS
RNDS[index]
uniformRnd
Generator&
operator()
operator[]
您可以通过以下方式解决此问题:
计算受保护部分内生成器的引用(或指针)(不能取决于容器的大小是否足够),以及
用一个 std::deque 代替 std::vector ,它在调整大小时不会使引用无效(除非通过调整大小从容器中删除引用的对象)。
std::deque
std::vector
这样的事情(关注竞争条件;还有其他事情,我可能会做不同的事情):
std::mt19937& get_generator(int index) { std::lock_guard<std::mutex> l(m); if (index <= RNDS.size()) RNDS.resize(index + 1); return RNDS[index]; } void run(){ int index = INDEX_GEN.fetch_add(1); auto& gen = get_generator(index); std::uniform_real_distribution<float> uniformRnd{ 0.0f, 1.0f }; while (true) { /* Do something with uniformRnd(gen); */ } }
[1]原型 operator() 的 uniformRnd 是 template< class Generator > result_type operator()( Generator& g ); 。换句话说,论证必须是a 易变的 引用,这意味着它不是隐式线程安全的;只要 const& 标准库函数的参数没有数据争用。
template< class Generator > result_type operator()( Generator& g );
const&