tags:

views:

283

answers:

5

Can I use a map or hashmap in a multithreaded program without needing a lock? i.e. are they thread safe?

I'm wanting to potentially add and delete from the map at the same time.

There seems to be a lot of conflicting information out there.

By the way, I'm using the STL library that comes with GCC under Ubuntu 10.04

EDIT: Just like the rest of the internet, I seem to be getting conflicting answers?

+5  A: 

No.

Honest. No.

edit

Ok, I'll qualify it.

You can have any number of threads reading the same map. This makes sense because reading it doesn't have any side-effects, so it can't matter whether anyone else is also doing it.

However, if you want to write to it, then you need to get exclusive access, which means preventing any other threads from writing or reading until you're done.

Your original question was about adding and removing in parallel. Since these are both writes, the answer to whether they're thread-safe is a simple, unambiguous "no".

Steven Sudit
An unqualified "no" with no other information is unlikely to be very helpful to someone asking questions at this level.
Nicholas Knight
@Nich: All qualified.
Steven Sudit
You don't have to prevent anyone from doing anything, you just have to be sure that they aren't reading or writing when you're writing.
Simon
@Simon: You can prevent them by ensuring that appropriate synchronization constructs are used.
Steven Sudit
@Steven Sudit: Yes, you can. You don't have to, but you can.
Simon
+11  A: 

You can safely perform simultaneous read operations, i.e. call const member functions. But you can't do any simultaneous operations if one of then involves writing, i.e. call of non-const member functions should be unique for the container and can't be mixed with any other calls.

i.e. you can't change the container from multiple threads. So you need to use lock/rw-lock to make the access safe.

Artyom
I think this answer this slightly confusing, from this answer it can be inferred that doing a multiple read while *one* write operation is going on is thread safe, which is not correct. Also, I think you meant *simultaneous* and not simulations?
Naveen
@Naveen cleared it a little.
Artyom
A: 

The answer (like most threading problems) is it will work most of the time. Unfortunately if you catch the map while it's resizing then you're going to end up in trouble. So no.

To get the best performance you'll need a multi stage lock. Firstly a read lock which allows accessors which can't modify the map and which can be held by multiple threads (more than one thread reading items is ok). Secondly a write lock which is exclusive which allows modification of the map in ways that could be unsafe (add, delete etc..).

edit Reader-writer locks are good but whether they're better than standard mutex depends on the usage pattern. I can't recommend either without knowing more. Profile both and see which best fits your needs.

Daniel
-1. To get the best performance you shouldn't need any locks at all.
Simon
@Simon: But then you need a lock-free data structure, which the ones in the standard library are not.
KeithB
+1 for pointing out that a lock allowing multiple readers but only one writer is potentially better than a full lock.
Steven Sudit
@KeithB: I agree that the STL-containers are not well suited for performant multithreaded use. To get the best performance you would have to implement locking-free multithreaded data-structures. Such as http://www.codeproject.com/KB/threads/LockFree.aspx#heading0005.
Simon
@Daniel, @Steven: Reader-writer locks may be sexy in theory, but in practice are often more expensive and dont scale as well as plain old locks. This is because the state associated with the reader-writer lock itself must be maintained atomically, and this generally boils down to locking a mutex at the O/S-primitive level. So you end up doing the same work as if you had just used a mutex in the first place.
John Dibling
@John: You may well be right, which is why I said "potentially better". There are cases when it really is better, such as when the usage is many parallel readers and only a rare writer, and the reader locks are not brief. Depending on the platform, the reader-writer lock may not suck: I know .NET came out with a newer one that sucks somewhat less.
Steven Sudit
"this generally boils down to locking a mutex at the O/S-primitive level."Huh, how ? What are CAS instructions for, then ?
kert
+2  A: 

The most commonly used model for STL containers' thread safety is the SGI one:

The SGI implementation of STL is thread-safe only in the sense that simultaneous accesses to distinct containers are safe, and simultaneous read accesses to to shared containers are safe.

but in the end it's up to the STL library authors - AFAIK the standard says nothing about STL's thread-safety.

But according to the docs GNU's stdc++ implementation follows it (as of gcc 3.0+), if a number of conditions are met.

HIH

Eugen Constantin Dinca
So what you're saying is. It might be under certain conditions. But I might be best not to rely on it?
Matt H
@Matt: What he is saying is that the standard doesn't prevent a library author from making containers thread-safe. Some implementations document to what extent they are thread-safe, but it is going to vary. I don't know of any that are multi-write safe by default.
KeithB
@Matt: KeithB summed up the answer very well
Eugen Constantin Dinca
+4  A: 

TBB is a free open-source library that provides thread-safe associative containers. (http://www.threadingbuildingblocks.org/)

ronag
Ok, but I don't want to use those.
Matt H
Don't see any reason why you would'nt if you want a solution with high performance. Otherwise you will have to use your own locks to protect the STL container of your choice from concurrent modification and access.
ronag
For one thing, it looks like they're charging money for it. I'd call that a negative.
Steven Sudit
How can "mymap[foo] = mymap[foo] + n;" ever be thread-safe without an explicit lock? Java dropped the thread-safe libraries for a reason!
Sjoerd
@Steven Sudit. It is open-source. They don't charge anything. I've been using it in an open-source project for a year and I'm very satisfied. They also provide excellent help in their forums.
ronag
@Sjoerd. They don't implemented the exact stl interface for that reason. Other than that they work just fine. Aren't you talking about the lock-free libraries?
ronag
@ronag: "mymap[foo]=mymap[foo]+n;" has a read and a write access to the same element. Without a lock, another thread could access the same element in the (short) time inbetween. Due to the nature of the statement, the map itself cannot create that lock on reading, as it doesn't know whether a write will follow or not. The only solution is to completely rewrite the interface, but you cannot handle things like "mymap[foo] = bar(mymap[foo]);" It gets even worse when that bar() function could access mymap[foo] as well. Solving the general case slows down your code too much to be generally useful.
Sjoerd
@ronag: Java had automatically-locking containers in the original Java. Then they found that "mymap[foo] = mymap[foo] + n;" still required explicit locking. So your code was slowed down by the locks inside the containers, while they didn't help! As a result, new versions of the containers, but without the locks, were added in a later version of Java.
Sjoerd
Interesting wasn't aware of that. Though, I don't understand what you are trying to say with your original comment?
ronag
The .NET ConcurrentDictionary class deals with this by offering methods with different signatures, including some with callbacks.
Steven Sudit