C++11 STL containers and thread safety - c++

I have trouble finding any up-to-date information on this.
Do C++11 versions of STL containers have some level of thread safety guaranteed?
I do expect that they don't, due to performance reasons. But then again, that's why we have both std::vector::operator[] and std::vector::at.

Since the existing answers don't cover it (only a comment does), I'll just mention 23.2.2 [container.requirements.dataraces] of the current C++ standard specification which says:
implementations are required to avoid data races when the contents of the contained object in different elements in the same sequence, excepting vector<bool>, are modified concurrently.
i.e. it's safe to access distinct elements of the same container, so for example you can have a global std::vector<std::future<int>> of ten elements and have ten threads which each write to a different element of the vector.
Apart from that, the same rules apply to containers as for the rest of the standard library (see 17.6.5.9 [res.on.data.races]), as Mr.C64's answer says, and additionally [container.requirements.dataraces] lists some non-const member functions of containers that can be called safely because they only return non-const references to elements, they don't actually modify anything (in general any non-const member function must be considered a modification.)

I think STL containers offer the following basic thread-safety guarantee:
simultaneous reads of the same object are OK
simultaneous read/writes of different objects are OK
But you have to use some form of custom synchronization (e.g. critical section) if you want to do something different, like e.g. simultaneous writes on the same object.

No. Check out PPL or Intel TBB for thread safe STL-like containers.
Like others have noted they have usual "multiple reader thread safety" but that is even pre C++11. Ofc this doesnt mean single writer multiple readers. It means 0 writers. :)

Related

c++ is it safe to read a map that may be written at the same time? [duplicate]

I have trouble finding any up-to-date information on this.
Do C++11 versions of STL containers have some level of thread safety guaranteed?
I do expect that they don't, due to performance reasons. But then again, that's why we have both std::vector::operator[] and std::vector::at.
Since the existing answers don't cover it (only a comment does), I'll just mention 23.2.2 [container.requirements.dataraces] of the current C++ standard specification which says:
implementations are required to avoid data races when the contents of the contained object in different elements in the same sequence, excepting vector<bool>, are modified concurrently.
i.e. it's safe to access distinct elements of the same container, so for example you can have a global std::vector<std::future<int>> of ten elements and have ten threads which each write to a different element of the vector.
Apart from that, the same rules apply to containers as for the rest of the standard library (see 17.6.5.9 [res.on.data.races]), as Mr.C64's answer says, and additionally [container.requirements.dataraces] lists some non-const member functions of containers that can be called safely because they only return non-const references to elements, they don't actually modify anything (in general any non-const member function must be considered a modification.)
I think STL containers offer the following basic thread-safety guarantee:
simultaneous reads of the same object are OK
simultaneous read/writes of different objects are OK
But you have to use some form of custom synchronization (e.g. critical section) if you want to do something different, like e.g. simultaneous writes on the same object.
No. Check out PPL or Intel TBB for thread safe STL-like containers.
Like others have noted they have usual "multiple reader thread safety" but that is even pre C++11. Ofc this doesnt mean single writer multiple readers. It means 0 writers. :)

What does the C++ standard library guarantee to avoid data races?

Reading C++11 FAQ -- Threads there's this paragraph which I don't understand:
Consequently, C++11 provides some rules/guarantees for the programmer to avoid data races:
A C++ standard library function shall not directly or indirectly access objects accessible by threads other than the current thread unless the objects are accessed directly or indirectly via the function's arguments, including this.
A C++ standard library function shall not directly or indirectly modify objects accessible by threads other than the current thread unless the objects are accessed directly or indirectly via the function's nonconst arguments, including this.
C++ standard library implementations are required to avoid data races when different elements in the same sequence are modified concurrently.
I know what a data race is, and multithreading generally, but I don't understand what these sentences are saying.
Could you explain them more clearly? Perhaps with an example? What is or isn't it safe for me (i.e. an application programmer) to do in a multi-threaded context?
Had I not read these, I would have guessed that it's not safe to have multiple threads calling a non-const method of an object of any type, but I suppose this is saying something in addition to that?
OK I think I figured it out from the comments (please correct me if I'm wrong).
These first two are similar -- i.e. that a function will only read or write memory that's reachable via the function argument.
Perhaps this means, no more and no less than, that functions won't read or mutate global or static data in their implementation.
There were a few functions in the C library which broke this rule, for example ctime.
ctime returns a pointer to static data and is not thread-safe.
The third is saying that a thread can mutate an element in a container while another thread accesses a different element.
This does not imply that it's also inherently safe to mutate the container, e.g. to insert a new element.
And vector<bool> is an exception to this rule:
std::vector<bool> behaves similarly to std::vector, but in order to be space efficient, it ... does not guarantee that different elements in the same container can be modified concurrently by different threads.

Is it safe to modify elements of std::valarray<T> concurrently?

If I've understood correctly, since C++11 it has been safe to call const member functions of a container concurrently and modify the elements of a container as long as the container itself is not modified as part of the operation (as seen from e.g. the table concerning thread safety in cppreference.com). Since std::valarray is not listed in the containers section of the (draft) standard, I'm unsure if thread safety also applies to it. In other words,
Is it safe to read from a std::valarray concurrently (in particular by using operator[] with slices)?
Is it safe to modify the elements of std::valarray<T> concurrently if the operation on T is safe?
I would like to use std::valarray for a multidimensional array of numbers that would be filled using multiple threads.
If I am reading your question correctly, [res.on.data.races] protects distinct slices from participating in data races, under
A C++ standard library function shall not directly or indirectly
access objects accessible by threads other than the current thread
unless the objects are accessed directly or indirectly via the
function's arguments, including this.
[container.requirements.dataraces] adds extra protection around modifications to distinct elements, which strictly valarray lacks.

Are c++ standard library containers like std::queue guaranteed to be reentrant?

I've seen people suggest that I should wrap standard containers such as std::queue and std::vector in a mutex lock or similar if i wish to use them.
I read that as needing a lock for each individual instance of a container being accessed by multiple threads, not per type or any utilization of the c++ standard library. But this assumes that the standard containers and standard library is guaranteed to be re-entrant.
Is there such a guarantee in the language?
The standard says:
Except where explicitly specified in this standard, it is implementation-defined which functions in the Standard C++ library may be recursively reentered.
Then it proceeds to specify that a function must be reentrant in, if I count them correctly, zero cases.
If one is to strictly follow the standard in this regard, the standard library suddenly becomes rather limited in its usefulness. A huge number of library functions call user-supplied functions. Writers of these functions, especially if those are themselves released as a library, in general don't know where they will be called from.
It is completely reasonable to assume that e.g. any constructor may be called from emplace_back of any standard container; if the user wishes to eliminate any uncertainty, he must refrain from any calls to emplace_back in any constructor. Any copy constructor is callable from e.g. vector::resize or sort, so one cannot manage vectors or do sorting in copy constructors. And so on, ad libitum.
This includes calling any third party component that might reasonably be using the standard library.
All these restrictions together probably mean that a large part of the standard library cannot be used in real world programs at all.
Update: this doesn't even start taking threads into consideration. With multiple threads, at least functions that deal with containers and algorithms must be reentrant. Imagine that std::vector::operator[] is not reentrant. This would mean that one cannot access two different vectors at the same time from two different threads! This is clearly not what the standard intends. I understand that this is your primary interest. To reiterate, no, I don't think there is reentrancy guarantee; and no, I don't think absence of such guarantee is reasonable in any way. --- end update.
My conclusion is that this is probably an oversight. The standard should mandate that all standard functions must be reentrant, unless otherwise specified.
I would
completely ignore the possibility of of any standard function being non-reentrant, except when it is clear that the function cannot be reasonably made reentrant.
raise an issue with the standards committee.
[Answer left for historical purposes, but see n.m.'s answer. There's no requirement on individual functions, but there is a single global non-requirement]
Yes, the standard guarantees reentrancy of member functions of standard containers.
Let me define what (non)-reentrancy means for functions. A reentrant function can be called with well-defined behavior on a thread while it is already on the call stack of that thread, i.e. executing. Obviously, this can only happen if the control flow temporarily left the reentrant function via a function call. If the behavior is not well-defined, the function is not reentrant.
(Leaf functions can't be said to be reentrant or non-reentrant, as the flow of control can only leave a leaf function by returning, but this isn't critical to the analysis).
Example:
int fac(int n) { return n==0 ? 1 : n * fac(n-1); }
The behavior of fac(3) is to return 6, even while fac(4) is running. Therefore, fac is reentrant.
The C++ Standard does define the behavior of member functions of standard containers. It also defines all restrictions under which such behavior is guaranteed. None of the member functions of standard containers have restrictions with respect to reentrancy. Therefore, any implementation which would restrict reentrancy is non-conformant.

What operations are thread-safe on std::map?

Suppose I have:
stl::map<std::string, Foo> myMap;
is the following function thread safe?
myMap["xyz"] ?
I.e. I want to have this giant read-only map that is shared among many threads; but I don't know if even searching it is thread safe.
Everything is written to once first.
Then after that, multiple threads read from it.
I'm trying to avoid locks to make this as faast as possible. (yaya possible premature optimization I know)
C++11 requires that all member functions declared as const are thread-safe for multiple readers.
Calling myMap["xyz"] is not thread-safe, as std::map::operator[] isn't declared as const.
Calling myMap.at("xyz") is thread-safe though, as std::map::at is declared as const.
In theory no STL containers are threadsafe. In practice reading is safe if the container is not being concurrently modified. ie the standard makes no specifications about threads. The next version of the standard will and IIUC it will then guarantee safe readonly behaviour.
If you are really concerned, use a sorted array with binary search.
At least in Microsoft's implementation, reading from containers is thread-safe (reference).
However, std::map::operator[] can modify data and is not declared const. You should instead use std::map::find, which is const, to get a const_iterator and dereference it.
Theoretically, read-only data structures and functions do not require any locks for thread-safety. It is inherently thread-safe. There are no data races on concurrent memory reads. However, you must guarantee safe initializations by only a single thread.
As Max S. pointed out, mostly implementation of reading an element in map like myMap["xyz"] would have no write operations. If so, then it is safe. But, once again, you must guarantee there is no thread which modifies the structure except the initialization phase.
STL collections aren't threadsafe, but it's fairly simple to add thread safety to one.
Your best bet is create a threadsafe wrapper around the collection in question.
This is an old question, but as all existing answers -and in particular the accepted one- are incorrect, I am adding another one (sorry, not enough rep for comments).
Because the OP explicitly mentions a read-only access (_"giant read-only map"), the [] operator should actually not be used since it can result in an insert. The at or find methods are suitable alternatives.
Some non-const functions are considered const in the context of thread-safety. at and find are such pseudo-const functions (this is explained very nicely in another question. More details are available from cppreference.com as well under the "thread-safety" section. And finally and see C++11 standard 23.2.2.1 for the official list).
As long as only const operations are used on the container the execution is thread-safe. As you initialize the huge map once (and do not modify it afterwards) before accessing it read-only from other threads, you are safe!