visitor
visitor

Reputation: 111

Is it safe to use a vector as a queue with multithreading?

Can a vector be written to by multiple threads without worrying about any potential race conditions between the threads trying to push_back a value at the same time? The underlying hardware/os will take care of everything and ensure theres no potential for the program crashing or failing right?

Just wanted to experiment with a simple example that didnt use locks mutexes or atomics.

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

using namespace std;

std::vector<int> myvector;


void append(int value)
{
  myvector.push_back(value);
}


int main()
{

 int val1=1;
 int val2=2;
 int val3=3;

 std::thread t1(append,val1);
 std::thread t2(append,val2);
 std::thread t3(append,val3);

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



 for(int x=0; x<myvector.size(); ++x)
        std::cout<<myvector[x]<<endl;


 return 0;

}

Running the program many times gives expected results of various combinations of push_backs by the threads

Upvotes: 0

Views: 507

Answers (2)

Gem Taylor
Gem Taylor

Reputation: 5613

None of the stl containers are thread safe, but it is easy enough to wrap them with mutexes. The issue is not for multiple reading as such, but when they are written to, it is generally dangerous to perform multiple mutate operations in parallel as internal pointers and counters will not be updated correctly; and it needs great care to read while mutate operations are occurring (potentially) on other threads.

std::vector is a particularly poor choice because the whole object is regularly at risk from resizing, leaving iterators broken even in single threading.

std::list variants are better because the elements are allocated individually, but you still have to put your own exclusion locking around any insert or delete, push or pop. Assuming you have only 1 list popper, if you know separately that last time you did not pop the list empty, then you can read the back without a mutex, before then popping it under a mutex against other threads pushing new elements on the front. If you popped the list empty last time, then you can't safely acquire the front() again without the mutex. In these circumstances you might want to implement a semaphore to awaken the popper thread when new data is added. On the other hand, if you have multiple readers and multiple writers, you can have the readers use a different mutex for getting to be the current popper, and still at least get to read the top without fighting the writers.

On the other hand if you take the simplistic approach of just mutexing all your accesses to the container, then it might as well be vector.

Upvotes: 1

Sam Varshavchik
Sam Varshavchik

Reputation: 118445

Neither std::vector, nor any of the containers in the C++ library are thread-safe. When using any container you will need to use mutexes and condition variables in order to correctly synchronize access to the container between threads.

The shown code is undefined behavior. Repeatedly varying the number of threads, and/or increasing the number of values pushed into a vector by the same thread, and/or otherwise loading the server with other processing loads, should eventually produce a crash or wrong results.

Upvotes: 1

Related Questions