There are two kinds of operations on C++ std
containers. Reader and Writer operations (these are not the terms the standard uses, but this reads easier). In addition, there are operations on elements in the container.
A const
method is a Reader method, as are "lookup" functions that are only non-const
because they return a non-const
reference to an element (or similar). There is a complete list in the standard, but common sense should work. vector::operator[]
, begin()
, map::find()
are all "Readers". map::operator[]
is not a "Reader".
You can have any number of threads engaging in Reader operations at the same time no problem.
If you have any thread engaged in a Writer operation, no other access can occur on the container.
You cannot safely have one Reader and one Writer at the same time. If you have any Writers, you must have excluded all other access.
You can safely have 1337 readers at once.
Operations on elements is somewhat similar, except that Writing to an element need only exclude other access to that element. And you are responsible for making your const
method play nice with each other. (the std
guarantees that the const
methods of the container will only call const
methods of the elements)
Note that changing sorting order in a std::
associative container is UB without modifying the container.
An iterator that is not invalidated, where you just operate on the element, will (I believe) count as operations on the element. Only synchronization on that element is required.
Note that std::vector<bool>
does not follow the above rules.
If you violate the above rules, the C++ std
library does not stop you. It just states there is a race condition -- aka, undefined behavior. Anything can happen. In C++ std
library, if you don't need something, you don't pay for it: and a single-threaded use of those containers would not need the weight of synchronization. So you don't get it by default.
A std::shared_timed_mutex
or std::experimental::shared_mutex
can both be useful to guarantee the above holds. unique_lock
for Writing, and shared_lock
for Reading. Write access to elements has to be shared_lock
ed on the container guarded, and somehow guarded against overlapping access to the same element without deadlock.
Iterator invalidation rules are relatively orthogonal to the thread-safety rules.