Pregunta

My question is if containers like vector, set, queue, map, multimap, etc. provide their standard thread-safety guarantees (i.e. that concurrent threads may call const methods, etc.) regardless of how a thread accesses the contained object.

To put it simply: if you lock, say, a map for read, can you safely (as far as the map is concerned) modify the contained objects (in this case the value) as long as you're not inserting or deleting items or otherwise calling non-const methods on the map?

¿Fue útil?

Solución

My question is if containers like vector, set, queue, map, multimap, etc. provide their standard thread-safety guarantees (i.e. that concurrent threads may access const members, etc.) regardless of how a thread accesses the contained object.

No, not "regardless". Given "concurrent threads [accessing] const members" [of the container], they can get const access to stored elements, but the container doesn't make it possible to do anything to the objects that wouldn't be legal if the objects were e.g. local variables - i.e. you can't call methods that affect mutable or static variables in a thread-unsafe way.

To put it simply: if you lock, say, a map for read, can you safely (as far as the map is concerned) modify the contained objects (in this case the value) as long as you're not inserting or deleting items or otherwise calling non-const methods on the map?

If by "lock a map for read" you mean your program has a separate read/write-lock and gets "reader" lock state before accessing the map, then no - you can't modify the contained objects if other readers may be accessing them. To make that safe, you need a mutex around the map usages, just as you would if the threads were operating on a local variable.


Examples

Below, a blank line separates examples, and the first and second columns list commands from two threads that may execute in either order or concurrently. Note that just because something is "safe" to execute doesn't mean the update will be visible in other threads until some explicit memory barrier or cache-flushing operation is done - it depends on your hardware: "proper" mutex/rwlocks etc. tend to take care of that.

class X { int n_; std::string s_; } x;

std::vector<X> v = ...;
std::map<int,X> m = ...;

thread 1                thread 2                  safe?

v.push_back(...);       ++v[0].n_;                Precondition: 1 <= size() < capacity()
                                                  (i.e. safe iff v[0] can't be moved)

v.some-const-member();  v.another-const-member(); YES - e.g. [n], find(), begin()

v[0].s_ = "hi";         std::cout << v[0].s_;     NO - as for any string var

v[0].s_.size();         std::cout << v[0].s_;     YES - as for any string var

rw_lock.r_lock() LOCKED
iterator i = m.find(7); rw_lock.w_lock() BLOCK
rw_lock.r_unlock()      ....
                        LOCKED
std::cout << i->second; m.insert(...);            YES - insert can't invalidate i
i->second.n_ += 3;      m.find(7).n_ -= 3;        NO - as per any int var

Otros consejos

I think this might already be answered in C++11 STL containers and thread safety. As long as you can guarantee that your threads never access the same elements as each other, then they can do whatever they like to them. The containers themselves do not have any guarantees of thread safety.

STL containers are not guaranteed to be thread-safe.

concurrent read is ok (you don't change elements in container or any property of container).

also pay attention to iterator validation, certain function invalidates part of or all iterators. (you can find information from cplusplus or cppreference)

as long as you lock the cotainer(use a mutex to guard the container) before accessing it, it is safe. if you need better performance,you may need to implement a rw_lock.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top