gpt4 book ai didi

c++ - 从多个线程访问随机数引擎

转载 作者:行者123 更新时间:2023-11-30 04:05:41 25 4
gpt4 key购买 nike

这是我的第一个问题,所以请原谅我违反您的政策的任何行为。我希望每个线程都有一个全局随机数引擎,为此我设计了以下方案:我启动的每个线程都从一个原子全局 int 中获取一个唯一索引。有一个随机引擎的静态 vector ,其第 i 个成员被认为由索引为 i 的线程使用。如果索引大于 vector 大小,元素将以同步方式添加到其中。为了防止性能损失,如果索引大于 vector 大小,我会检查两次:一次以不同步的方式,一次在锁定互斥体之后。到目前为止一切顺利,但以下示例因各种错误(堆损坏、malloc 错误等)而失败。

#include<vector>
#include<thread>
#include<mutex>
#include<atomic>
#include<random>
#include<iostream>

using std::cout;

std::atomic_uint INDEX_GEN{};
std::vector<std::mt19937> RNDS{};
float f = 0.0f;
std::mutex m{};

class TestAThread {
public:
TestAThread() :thread(nullptr){
cout << "Calling constructor TestAThread\n";
thread = new std::thread(&TestAThread::run, this);
}

TestAThread(TestAThread&& source) : thread(source.thread){
source.thread = nullptr;
cout << "Calling move constructor TestAThread. My ptr is " << thread << ". Source ptr is" << source.thread << "\n";
}

TestAThread(const TestAThread& source) = delete;

~TestAThread() {
cout << "Calling destructor TestAThread. Pointer is " << thread << "\n";
if (thread != nullptr){
cout << "Deleting thread pointer\n";
thread->join();
delete thread;
thread = nullptr;
}
}

void run(){
int index = INDEX_GEN.fetch_add(1);
std::uniform_real_distribution<float> uniformRnd{ 0.0f, 1.0f };

while (true){
if (index >= RNDS.size()){
m.lock();
// add randoms in a synchronized manner.
while (index >= RNDS.size()){
cout << "index is " << index << ", size is " << RNDS.size() << std::endl;
RNDS.emplace_back();
}
m.unlock();
}

f += uniformRnd(RNDS[index]);
}
}

std::thread* thread;
};

int main(int argc, char* argv[]){
std::vector<TestAThread> threads;
for (int i = 0; i < 10; ++i){
threads.emplace_back();
}

cout << f;
}

我做错了什么?!

最佳答案

显然 f += ...无论右侧如何,都将是一个竞争条件,但我想你已经知道了。

我看到的主要问题是您对全局 std::vector<std::mt19937> RNDS 的使用.您的受互斥锁保护的临界区仅包含添加新元素;不访问现有元素:

... uniformRnd(RNDS[index]);

这不是线程安全的,因为调整大小 RNDS在另一个线程中可能会导致 RNDS[index]被移动到一个新的内存位置。事实上,这可能发生在引用 RNDS[index] 之后。已计算但在 uniformRnd 之前开始使用它,在这种情况下是什么 uniformRnd认为是 Generator&将是一个悬空指针,可能指向一个新创建的对象。无论如何,uniformRndoperator()不保证数据竞争 [注 1],RNDS 也不保证的 operator[] .

您可以通过以下方式解决此问题:

  1. 计算对 protected 部分中生成器的引用(或指针)(这不能取决于容器的大小是否足够),以及

  2. 使用 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() 的原型(prototype)的 uniformRndtemplate< class Generator > result_type operator()( Generator& g ); .换句话说,参数必须是一个可变引用,这意味着它不是隐式线程安全的;只有const&标准库函数的参数没有数据竞争。

关于c++ - 从多个线程访问随机数引擎,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23172713/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com