Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Multiple threads add elements concurrently on different vectors of the same vector occurs error

#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;
}

This code occurs some errors (like segmentation fault)

As I wrote above, struct B has a vector of struct A and struct A has a vector of int.

  • Step 1) Each threads pushes new struct A element to the same vector (b.b) with critical section.

  • Step 2) After then, each thread pushes new int element to the vector a of struct A each created without critical section.

I thought pushing new element to the same vector concurrently should occurs some problems, but pushing new element to the different vectors concurrently should not occur errors.

If I put whole work function into the critical section, it doesn't occur error.

So, I concluded pushing new element to the different vectors will not occur error, BUT if they are in the same vector, it occurs error.

But I cannot explain the reason. Someone please tell me about this. :(

like image 953
Hoon Avatar asked Mar 12 '20 09:03

Hoon


2 Answers

When the second thread pushes a new value to b.b this vector might get resized. If it gets resized all references to it's elements are getting invalidated. So the reference A& a of the first thread gets invalidated.

You could

  • use a std::list (linked list)
  • resize() the vector b.b before working with it, so it doesn't need to be resized later (or reserve(), doesn't make much of a difference here)
like image 122
churill Avatar answered Sep 29 '22 11:09

churill


std::vector reallocates its memory when you push_back, because it needs additional memory. If you reserve() the vector, it won't reallocate until it needs additional memory.

like image 41
Andy Avatar answered Sep 29 '22 11:09

Andy