tags:

views:

133

answers:

4

I am using a queue to communicate between threads. I have one reader and multiple writer threads. My question is do I need to lock the queue every time when I use push/front/pop from the queue for the reader? Can I do something like the following:

//reader threads
getLock();
get the number of elements from the queue
releaseLock();

int i = 0;
while( i < numOfElements){
    queue.front();
    queue.pop();
    i++
}

The idea is that I want to reduce the granularity of the locked code and since the writer thread would only write to the back of the queue and there is only a single reader thread. As long as I get the number of elements, then I could get the elements from the queue OR do I need to enclose the front() and pop() in the lock as well?

Thanks.

+3  A: 

Any type that doesn't explicitly state its thread-safety guarantees should always be controlled by a mutex. That said, your implementation's stdlib may allow some variation of this — but you can't know for all implementations of std::queue.

As std::queue wraps another container (it's a container adapter), you need to look at the underlying container, which defaults to deque.

You may find it easier, better, or more portable to write your own container adapter that makes the guarantees you need. I don't know of anything that does this exactly for a queue in Boost.

I haven't looked at C++0x enough to know if it has any solution for this out-of-the-box, but that could be another option.

Roger Pate
C++0x has atomics, so it enables programmers to write lock-free (thread-safe) algorithms, and provides standard mutex's, but it doesn't have anything out-of-the-box.
GMan
+1  A: 

This is absolutely implementation-dependent. The C++ standard makes on mention about threads or thread safety, so whether or not this will work depends on how your implementation handles queue elements.

In your case, the reader is actually popping the queue, which is considered a write operation. I doubt any of the common implementations actually guarantee thread-safety in this case, when multiple threads simultaneously write to a container. At least VC++ does not:

For reads to the same object, the object is thread safe for reading when no writers on other threads.

For writes to the same object, the object is thread safe for writing from one thread when no readers on other threads.

casablanca
+1  A: 

As others have already mentioned, standard containers are not required to guarantee thread safety so what you're asking for cannot be implemented portably. You can reduce the time your reader thread is locking the writers out by using 2 queues and a queue pointer that indicates the queue that is currently in use by the writers.

Each writer would:

  • Acquire lock
  • Push element(s) into the queue currently pointed to by the queue pointer
  • Release lock

The reader can then do the following:

  • Acquire lock
  • Switch queue pointer to point to the second queue
  • Release lock
  • Process elements from the first queue
Praetorian
How would the writer work?
GMan
The writer would always append to whatever queue the queue pointer is currently pointing to (after first acquiring the lock). In this scenario, the lock is protecting the whichever queue the pointer currently refers to (and the pointer itself); the other queue ("first queue") can be processed by the reader w/o holding the lock.
David Gelhar
+1  A: 

Sometimes you can resolve a lot of concurrency headache by avoiding sharing state or resources among threads. If you have multiple threads that access a container concurrently in order to push in their work then try to have them work on dedicated containers. At specific points you then collect the containers' elements onto the central container in a non-concurrent manner.

If you can avoid sharing state or resources among threads then you have no problem running threads concurrently. Threads then need not worry about each other, because they are completely isolated and bear no effect whatsoever on each other.

wilhelmtell