C++ do we need mutex when using map? - c++

I have two threads, one is inserting and another is deleting an entry in the map. I am wondering whether I have a mutex around these function calls? And also one thread incrementing a counter inside this map and the other decrementing that counter. Do I need mutex for that as well?
Thanks,

Changes to the map itself (insertions, deletions) need to be synchronized. The same is true for traversal and lookup (i.e. begin(), find(), [], etc.).
Multiple threads can access different elements safely, though.

If you are incrementing and decrementing the SAME element in the map (or what may be the same element and you can't say for sure), then you need to have some sort of synchronisation. You could use an std::atomic<int> to avoid having to use a mutex tho'.
Any insert or remove in the tree will need to be protected with a mutex or similar - and of course, that also means that any access to the content of the tree needs to be protected in the same way, so if you use std::map<T>::iterator (at least for erase in the tree) will be invalidated. So you really need to ensure that no erase happens when you use any other access to the tree. This includes "ready-made" functions such as find.

Related

std::map write/read from multiple threads

I want to be able to read and write in a std::map from multiple threads. Is there a way to do that without mutex (maybe with std::atomic)?
If not, what's the simplest way to do that in C++11?
If the values are std::atomic<>, you can modify and read them from arbitrary threads, but you'll need to have saved their addresses somehow - pointers, references or iterators - and you'll need to know no other thread will call erase on them....
Still, even atomic values won't make it safe to modify the container (e.g. inserting or erasing elements), while other threads are also doing modifications or lookups or iteration. Even the "const" functions - like size(), empty(), begin(), end(), count(), and iterator movement - are unsafe, because the mutating operations may be in the middle of rewiring the inter-node links or updating the same data.
For anything more than the above, you will need a mutex.
For a concrete example, say you've inserted a node with std::string key "client_counter" - you could start a thread that gets an iterator to that element and does atomic updates to the counter, while other threads can find the element and read from it but must not erase it. You could still have other nodes inserted in the map, with other updaters and readers, without any extra synchronisation with the client_counter-updating thread.
If you don't want to use mutex then you need to wait for concurrent containers (C++17?). If you want to use std::atomic operations inside std::map then you probably want to make or found on the Internet fully implementation of concurrent atomic std::map.
If you want to use std::map of std::atomic then you probably need to know that this will protect only elements inside std::map, but not std::map in self.

Mutithreading accessing one std::map , will cause unsafe behavior?

If more than one thread access one map object, but, I can make sure any of these threads accessing will not have the same key, and the accessing is just like:
//find value by key
//if find
// erase the object or change the value
//else
// add new object of the key
Will the operation cause synchronization problem?
Yes, doing concurrent updates without proper synchronization may cause crashes, even if your threads access different keys: the std::map is based on trees, trees get rebalanced, so you can cause a write to a parent of a node with a seemingly unrelated key.
Moreover, it is not safe to perform read-only access concurrently with writing, or searching unlocked + locking on write: if you have threads that may update or delete nodes, you must lock out all readers before you write.
You will have concurrency problems if any of the threads inserts into the tree. STL map is implemented using a red-black tree (or at least that's what I'm familiar with — I don't know whether the Standard mandates red-black tree). Red-black trees may be rebalanced upon insert, which would lead to all sorts of races between threads.
Read-only access (absolutely no writers) would be fine, but keep in mind operator[] is not read-only; it potentially adds a new element. You'd need to use the find() method, get the iterator, and derefence it yourself.
Unless the docs (ie, the ISO C++11 standard) say it's thread-safe (and they don't), then that's it. Period. It's not thread-safe.
There may be implementations of a std::map that would allow this but it's by no means portable.
Maps are often built on red-black trees or other auto-balancing data structures so that a modification to the structure (such as inserting or deleting a key) will cause rebalancing.
You should wrap read and write operations on the map with something like a mutex semaphore, to ensure that synchronisation is done correctly.

Thread safety in boost::unordered_map of std::string and std::list, while making changes to list

I am using a boost::unordered_map<const std::string, std::list<TypeA> > in a performance critical multi-threaded environment. I understand that writing to STL containers isn't thread safe and the same goes for boost::unordered_map.
boost::unordered_map<const std::string, std::list<TypeA> > myMap;
// Added some elements to myMap
Now, if I want to add or delete an element of type A to the list as , is it necessary is I just lock the entire map as opposed to locking the list that is being modified so that other threads can read/write the rest of the key value pairs?
// Assuming there are pair with the keys "Apple" and "Orange" in myMap
A a, b;
myMap["Orange"].push_back(a) //Add an element to the list
myMap["Apple"].remove(b); //Remove an element
What if the list is replaced by another STL container?
Thanks.
Since you're modifying only the contained object, not the [unordered_]map itself, you should only have to lock that contained object. If you change the list to another sequence (e.g., deque or vector) the same should remain true -- changing the type of the contained object doesn't change the fact that you're only modifying that contained object, not the map that contains it.
You don't have to perform any locking here. If it's guaranteed that the keys already exist, then accessing them is a non-mutating operation which doesn't need locking (as long as nobody else is mutating). And each list is independent- as long as nobody else is accessing myMap["Apple"] at the same time, you're golden. Of course, you could simply use something more suited to the task, like a lockless list, which can be safely mutated from multiple threads, or a concurrent_unordered_map, such as you can find in TBB or PPL.

Thread-safety of c++ maps

This is about thread safety of std::map. Now, simultaneous reads are thread-safe but writes are not. My question is that if I add unique element to the map everytime, will that be thread-safe?
So, for an example, If I have a map like this std:map<int, std::string> myMap
and I always add new keys and never modify the existing key-value, will that be thread-safe?
More importantly, will that give me any random run-time behavior?
Is adding new keys also considered modification? If the keys are always different while adding, shouldn't it be thread-safe as it modifies an independent part of the memory?
Thanks
Shiv
1) Of course not
2) Yes, I hope you'll encounter it during testing, not later
3) Yes, it is. The new element is added in a different location, but many pointers are modified during that.
The map is implemented by some sort of tree in most if not all implementations. Inserting a new element in a tree modifies it by rearranging nodes by means of resetting pointers to point to different nodes. So it is not thread safe
no, yes, yes. You need to obtain exclusive lock when modifying container (including insertion of new keys), though while there's no modification going on you can, of course, safely read concurrently.
edit: http://www.sgi.com/tech/stl/thread_safety.html might be of interest for you.

Am I going to be OK for threading with STL given these conditions?

I have a collection of the form:
map<key, list<object> >
I only ever insert at the back of the list and sometimes I read from the entire map (but I never write to the map, except at initialization).
As I understand it, none of the STL containers are thread safe, but I can only really have a maximum of one thread per key. Am I missing anything in assuming I'll be pretty safe with this arrangement?
If the map is never modified at all during the multi-threaded scenario, then you're fine. If each thread looks at its own list, then that's thread-private data, so you're also fine.
Take care not to try and lookup keys with [] because that will insert (modify) if the key doesn't exist in the map yet.
However, I'm curious as to why you'd need this structure - why not keep a pointer/reference or the actual list object itself on the stack of each thread, given that it's private to each thread?
(If it's not, then you need proper synchronisation on the list.)
In fact you say you "read from the entire map" - presumably meaning that any random thread may try to iterate through any of the lists. So you definitely need to synchronise operations on the lists.
TBH as long as you put a critical section around any write and read it will work fine.