C++:在多线程时对访问 class 数据成员感到困惑
C++: confusion about accessing class data members while multithreading
我有以下最小工作示例,其中我在向量 chains
中创建了多个 markov_chain
个对象,并在向量 [=16] 中创建了相同数量的 thread
个对象=],每个对象在每个对应的 markov_chain
对象上执行一个 markov_chain
class 成员函数 sample
。此函数接受一些整数(在下面的示例中为 99)并将其分配给 markov_chain
对象的 acceptance
public 数据成员。然后我为向量中的每个对象打印 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
的值。我不知道为什么会这样;当我在没有创建线程的情况下使用它时,函数 sample
成功分配了所需的值。
您的代码有 2 个问题:
在创建每个 std::thread
时,您正在传递每个对象的 副本 作为 [=15= 的 this
参数].
以您的方式将多个对象推入 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;
}
我有以下最小工作示例,其中我在向量 chains
中创建了多个 markov_chain
个对象,并在向量 [=16] 中创建了相同数量的 thread
个对象=],每个对象在每个对应的 markov_chain
对象上执行一个 markov_chain
class 成员函数 sample
。此函数接受一些整数(在下面的示例中为 99)并将其分配给 markov_chain
对象的 acceptance
public 数据成员。然后我为向量中的每个对象打印 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
的值。我不知道为什么会这样;当我在没有创建线程的情况下使用它时,函数 sample
成功分配了所需的值。
您的代码有 2 个问题:
在创建每个
std::thread
时,您正在传递每个对象的 副本 作为 [=15= 的this
参数].以您的方式将多个对象推入
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;
}