C++:在多线程处理时访问类数据成员的混淆

C++: confusion about accessing class data members while multithreading

本文关键字:数据成员 访问 多线程处理 C++      更新时间:2024-09-29

我有以下最小工作示例,其中我在向量chains中创建了多个markov_chain对象,在向量workers中创建了相等数量的thread对象,每个对象在相应的markov_chain对象上执行markov_chain类成员函数sample。此函数获取某个整数(以下示例中为99(,并将其分配给markov_chain对象的acceptance公共数据成员。然后,我为矢量中的每个对象打印acceptance的值。

#include <iostream>
#include <thread>
#include <algorithm>
#include <vector>

class markov_chain 
{
public:
unsigned int length{0}, acceptance{0};
markov_chain(unsigned int l) {length=l;}
~markov_chain() {}
void sample(int acc);
};
void markov_chain::sample(int acc)
{
acceptance = acc;
std::cout << length << ' ' << acceptance << std::endl;
}
int main()  
{
int number_of_threads{3};
int number_of_samples{1000};
std::vector<markov_chain> chains;
std::vector<std::thread> workers;
for (int i = 0; i <= number_of_threads; i++) {
chains.push_back(markov_chain(number_of_samples));
workers.push_back(std::thread(&markov_chain::sample, chains[i], 99));
}
std::for_each(workers.begin(), workers.end(), [](std::thread &t) 
{
t.join();
});
for (int i = 0; i <= number_of_threads; i++) {
std::cout << chains[i].length << ' ' << chains[i].acceptance << std::endl;
}
return 0;
}

执行后,程序输出

1000 99
1000 99
1000 99
1000 99
1000 0
1000 0
1000 0
1000 0

因此,程序未能更改向量chains中对象的acceptance的值。我不知道为什么会发生这种事;当我在不创建线程的情况下使用函数CCD_ 13时。

您的代码有两个问题:

  1. 在创建每个std::thread时,您将传递每个对象的副本作为sample()this参数。

  2. 按照您的方式将多个对象推入chains向量可能会导致向量重新分配其内部数组,从而使您已经传递给现有线程的任何对象指针无效,因为这些原始对象在重新分配后就不见了。

在创建任何线程之前,您需要完全初始化chains向量。您需要将指向每个对象的指针传递给每个线程。

您可以提前reserve()阵列,以避免在推入时重新分配,例如:

int main()  
{
int number_of_threads{3};
int number_of_samples{1000};
std::vector<markov_chain> chains;
std::vector<std::thread> workers;
chains.reserve(number_of_threads);
for (int i = 0; i < number_of_threads; ++i) {
chains.push_back(markov_chain(number_of_samples));
workers.push_back(std::thread(&markov_chain::sample, &chains[i], 99));
}
for(auto &t : workers) {
t.join();
}
for (auto &c : chains) {
std::cout << c.length << ' ' << c.acceptance << std::endl;
}
return 0;
}

演示

然而,由于所有对象都是用相同的起始值初始化的,因此一种更简单的方法是完全去掉chains.push_back(),转而使用chains.resize(),例如:

int main()  
{
int number_of_threads{3};
int number_of_samples{1000};
std::vector<markov_chain> chains;
std::vector<std::thread> workers;
chains.resize(number_of_threads, markov_chain(number_of_samples));
for (int i = 0; i < number_of_threads; ++i) {
workers.push_back(std::thread(&markov_chain::sample, &chains[i], 99));
}
for(auto &t : workers) {
t.join();
}
for (auto &c : chains) {
std::cout << c.length << ' ' << c.acceptance << std::endl;
}
return 0;
}

演示

或者,甚至使用vector构造函数本身:

int main()  
{
int number_of_threads{3};
int number_of_samples{1000};
std::vector<markov_chain> chains(number_of_threads, markov_chain(number_of_samples));
std::vector<std::thread> workers;
for (int i = 0; i < number_of_threads; ++i) {
workers.push_back(std::thread(&markov_chain::sample, &chains[i], 99));
}
for(auto &t : workers) {
t.join();
}
for (auto &c : chains) {
std::cout << c.length << ' ' << c.acceptance << std::endl;
}
return 0;
}

演示