bisarch
bisarch

Reputation: 1398

C++ STL vector iterator vs indexes access and thread safety

I am iterating over an STL vector and reading values from it. There is another thread which can make changes to this vector. Now, if the other thread inserts or removes and element from the vector, it invalidates the iterator. There is no use of locks involved. Does my choice of accessing the container through indexes(Approach 1) in place of iterators(Approach 2) make it thread safe? What about performance?

struct A{int i; int j;};

Approach 1:

   size_t s = v.size();//v contains pointers to objects of type A
    for(size_t i = 0; i < s; ++i)
    {
         A* ptr = v[i];
         ptr->i++;
    }

Approach 2:

std::vector<A*>::iterator begin =  v.begin();
std::vector<A*>::iterator end =  v.end();
for(std::vector<A*>::iterator it = begin; it != end; ++it)
{
     A* ptr = *it;
     ptr->i++: 
}

Upvotes: 4

Views: 7220

Answers (4)

user3726672
user3726672

Reputation: 315

Could your algorithm work with a fixed size array?

Reason I ask is that the only way, logically, to have multiple threads modifying (most kinds of) container in a thread-safe, lock-free way is to make the container itself invariant. That means the CONTAINER doesn't ever change within the threads, just the elements within it. Think of the difference between messing with the insides of boxcars on a train, vs. actually adding & removing entire boxcars FROM that train as its moving down the tracks. Even meddling with the elements is only safe if your operations on that data observe certain constraints.

Good news is that locks are not always the end of the world. If multiple execution contexts (threads, programs, etc.) can hit the same object simultaneously, they're often the only solution anyway.

Upvotes: 0

user1408985
user1408985

Reputation:

OP "Does my choice of accessing the container through indexes(Approach 1) in place of iterators(Approach 2) make it thread safe?"

No, neither approach is thread safe once you start writing to your data structure.

Therefore you will need to serialize access to your data structure.

To save you a lot of time and frustration there a lot of ready-rolled solutions e.g.

Intel Threading Building Blocks (TBB) which comes with thread safe containers such as concurrent_vector.

http://threadingbuildingblocks.org/

A concurrent_vector is a container with the following features:

  • Random access by index. The index of the first element is zero.
  • Multiple threads can grow the container and append new elements concurrently.
  • Growing the container does not invalidate existing iterators or indices.*

OP "What about performance?"

Not knowable. Different performance on different systems with different compilers but not known to be large enough to influence your choices.

Upvotes: 3

Dietmar K&#252;hl
Dietmar K&#252;hl

Reputation: 153820

The thread-safety guarantees for standard library containers are very straight forward (these rules were added in C++ 2011 but essentially all current library implementations conform to these requirements and impose the corresponding restrictions):

  1. it is OK to have multiple concurrent readers
  2. if there is one thread modifying a container there shall be no other thread accessing (reading or writing) it
  3. the requirements are per container object

Effectively, this means that you need to use some mechanism external to the container to guarantee that a container accessed from multiple threads is handled correctly. For example, you can use a mutex or a readerwriter lock. Of course, most of the time containers are accessed only from one thread and things work just fine without any locking.

Without using explict locks you will cause data races and the behavior is undefined, independent of whether you use indices or iterators.

Upvotes: 4

mfontanini
mfontanini

Reputation: 21900

No. STL containers are not thread safe.

You should provide exclusive access to each thread(the one that removes/the one that adds), while they're accessing the vector. Even when using indexes, you might be removing the i-th elemenet, making the pointer you had retrieved, invalid.

Upvotes: 3

Related Questions