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 个问题:

  1. 在创建每个 std::thread 时,您正在传递每个对象的 副本 作为 [=15= 的 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;
}

Demo

但是,由于所有对象都使用相同的起始值进行初始化,因此更简单的方法是完全摆脱 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;
}

Demo

或者,甚至使用 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;
}

Demo