Несколько потоков добавляют элементы одновременно на разных векторах одного вектора происходит ошибка - PullRequest
5 голосов
/ 12 марта 2020
#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;
}

В этом коде возникают некоторые ошибки (например, ошибка сегментации)

Как я писал выше, struct B имеет вектор struct A, а struct A имеет вектор int.

  • Шаг 1) Каждый поток добавляет новый элемент struct A к одному и тому же вектору (b.b) с критическим сечением.

  • Шаг 2) После этого каждый поток помещает новый элемент int в вектор a из struct A, каждый из которых создан без критической секции.

Я думал, что одновременный перенос нового элемента в один и тот же вектор должен возникают некоторые проблемы, но одновременное нажатие нового элемента в разные векторы не должно приводить к ошибкам.

Если я помещу целую функцию work в критическую секцию, ошибки не возникает.

Итак, я пришел к выводу, что при добавлении нового элемента в разные векторы не возникнет ошибка, НО, если они находятся в одном и том же векторе, возникнет ошибка.

Но я не могу объяснить причину. Кто-нибудь, пожалуйста, расскажите мне об этом. (

Ответы [ 2 ]

5 голосов
/ 12 марта 2020

Когда второй поток помещает новое значение в b.b, этот вектор может измениться. При изменении размера все ссылки на его элементы становятся недействительными. Таким образом, ссылка A& a первого потока становится недействительной.

Вы можете

  • использовать std::list (связанный список)
  • resize() вектор b.b, прежде чем работать с ним, поэтому его не нужно будет изменять позже (или reserve(), здесь нет большой разницы)
1 голос
/ 12 марта 2020

std::vector перераспределяет свою память, когда вы push_back, потому что ему нужна дополнительная память. Если вы зарезервируете () вектор, он не будет перераспределяться, пока ему не понадобится дополнительная память.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...