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.
Related
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. :)
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.
I have a std::vector<std::atomic<size_t>> vec. Is it safe to run vec[index].fetch_add(1, std::memory_order_release) or store/load with multiple concurrent threads on it? I think it should be, because reading is thread safe and writing to one entry at the same time from multiple threads is impossible because of the atomics - is that right?
No it is not, in general, thread safe since the container itself is not atomic.
That said, so long as you don't change what is in the vector (i.e. doing anything that invalidates the return of data()) , you'll be fine.
Sadly you can't resort to std::atomic<std::vector<...>> as a std::vector is not trivially copyable.
vec[X].fetch_add is safe as long as you're modifying each individual atomic. If you call any non-const (modifying) method of the wrapping std::vector this is undefined behaviour, as std::vector itself is not thread safe.
De-facto, you can initialize the vector in some thread, pass its reference to some asynchronous task and from that point, only act on specific elements of the vector, not act on the vector itself. vec[X].action(...) is thread safe, vec.action(...) is not.
It depends what the other threads are doing.
From the standard:
17.6.5.9 Data race avoidance [res.on.data.races]
...
2 A C++ standard library function shall not directly or indirectly access objects (1.10) accessible by threads
other than the current thread unless the objects are accessed directly or indirectly via the function’s arguments,
including this.
3 A C++ standard library function shall not directly or indirectly modify objects (1.10) accessible by threads
other than the current thread unless the objects are accessed directly or indirectly via the function’s non-const
arguments, including this.
and
23.2.2 Container data races [container.requirements.dataraces]
1 For purposes of avoiding data races (17.6.5.9), implementations shall consider the following functions to be
const: begin, end, rbegin, rend, front, back, data, find, lower_bound, upper_bound, equal_range, at
and, except in associative or unordered associative containers, operator[].
2 Notwithstanding (17.6.5.9), implementations are required to avoid data races when the contents of the contained
object in different elements in the same container, excepting vector<bool>, are modified concurrently.
By combining these two parts with the rules for atomics, we can deduce that calling vec[index].fetch_add(1, std::memory_order_release) cannot cause a race condition with other threads performing the same or other "const" operations (including those mentioned in paragraph 23.2.2.1). However, if another thread invokes a non-const operation on vec itself (e.g. insert, erase, resize etc.) then we experience undefined behaviour as specified by section 1.10:
1.10 Multi-threaded executions and data races [intro.multithread]
...
4 Two expression evaluations conflict if one of them modifies a memory location (1.7) and the other one accesses
or modifies the same memory location.
...
21 The execution of a program contains a data race if it contains two conflicting actions in different threads,
at least one of which is not atomic, and neither happens before the other. Any such data race results in
undefined behavior.
Your expression vec[n].atomic_op(...) is not itself atomic, but decomposes into:
auto iter = vec.begin();
iter += n;
iter->atomic_op(...);
So, the first two statements are vulnerable to the usual iterator invalidation rules. Concurrently changing the size of the vector, or erasing any element before the nth, or the nth element itself, can break them.
Once you have an iterator to an element, if that iterator is not invalidated during the time you're using it, then whatever atomic operations you perform on the element itself will be safe.
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. :)
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!