多个线程在同一个向量的不同向量上并发添加元素发生错误

Hoo*_*oon 5 c++ multithreading vector

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

struct A {
    std::vector<int> a;
};

struct B{
    std::vector<A> b;
    std::mutex mtx;
};

void work(int id, struct B& b) {
    std::unique_lock<std::mutex> lck(b.mtx);

    b.b.push_back(A());
    struct A& a = b.b.back();

    lck.unlock();

    for(int i = 0; i < 1000; i++) {
        std::cout << id << " " << i << std::endl;
        a.a.push_back(i);
    }
}

int main() {
    struct B b;
    std::thread t1, t2;

    t1 = std::thread([&] {
        work(1, b);
    });

    t2 = std::thread([&] {
        work(2, b);
    });

    t1.join();
    t2.join();

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

此代码出现一些错误(如分段错误)

正如我上面写的,struct B有一个向量struct Astruct A有一个向量int

  • 步骤 1) 每个线程将新struct A元素推送到b.b具有临界区的同一个向量 ( )。

  • 步骤 2) 之后,每个线程将新int元素推送到每个创建的没有临界区的向量astruct A

我认为将新元素同时推送到同一个向量应该会出现一些问题,但是将新元素同时推送到不同的向量不应该发生错误。

如果我将整个work函数放入临界区,则不会发生错误。

所以,我得出结论,将新元素推送到不同的向量不会发生错误,但是如果它们在同一个向量中,则会发生错误。

但我无法解释原因。有人请告诉我这件事。:(

chu*_*ill 5

当第二个线程b.b向该向量推送新值时,可能会调整其大小。如果它被调整大小,所有对它的元素的引用都会失效。所以A& a第一个线程的引用无效。

你可以

  • 使用std::list(链表)
  • resize()向量b.b在使用它之前,所以以后不需要调整它的大小(或者reserve(),在这里没有太大区别)