1

I am planning to do the following:

store a deque of pre-built objects to be consumed. The main thread might consume these objects here and there. I have another junky thread used for logging and other not time-critical but expensive things. When the pre-built objects are running low, I will refill them in the junky thread.

Now my question is, is there going to be race condition here? Technically one thread is consuming objects from the front, and another thread is pushing objects into the back. As long as I don't let the size run down to zero, it should be fine. The only thing that concerns me is the "size" of this deque. Do they store a integer "size" variable in STL containers? should modifying that size variable introduce race conditions?

What's the best way of solving this problem? I don't really want to use locks, because the main thread is performance critical (the reason I pre-built these objects in the first place!)

CuriousMind
  • 15,168
  • 20
  • 82
  • 120

4 Answers4

8

STL containers are not thread safe, period, don't play with this. Specifically the deque elements are usually stored in a chain of short arrays and that chain will be modified when operating with the deque, so there's a lot of room for messing things up.

sharptooth
  • 167,383
  • 100
  • 513
  • 979
4

Another option would be to have 2 deques, one for read another for write. The main thread reads, and the other writes. When the read deque is empty, switch the deques (just move 2 pointers), which would involve a lock, but only occasionally.

The consumer thread would drive the switch so it would only need to do a lock when switching. The producer thread would need to lock per write in case the switch happens in the middle of a write, but as you mention the consumer is less performance-critical, so no worries there.

What you're suggesting regarding no locks is indeed dangerous as others mention.

Brady
  • 10,207
  • 2
  • 20
  • 59
  • -1 But what if consumer thread is in the middle of reading from the queue when the switch occurs? To prevent that, you'd have to protect all reads as well, which pretty much defeats the whole idea. Ditto for producer that might be in the middle of writing during the switch. – Branko Dimitrijevic Jun 01 '12 at 13:44
  • @BrankoDimitrijevic, that's why you wait until the read deque is ***empty***, then block just for the switch. If the producer thread is not performance critical (like OP mentions), it can block on each write. The switch is driven from the consumer. – Brady Jun 01 '12 at 13:46
  • I understand now. Sorry for the downvote - can you edit the answer so I can reverse it? – Branko Dimitrijevic Jun 01 '12 at 13:53
1

As @sharptooth mentioned, STL containers aren't thread-safe. Are you using a C++11 capable compiler? If so, you could implement a lock-free queue using atomic types. Otherwise you'd need to use assembler for compare-and-swap or use a platform specific API (see here). See this question to get information on how to do this.

I would emphasise that you should measure performance when using standard thread synchronisation and see if you do actually need a lock-free technique.

Community
  • 1
  • 1
TheJuice
  • 4,434
  • 2
  • 26
  • 36
0

There will be a data race even with non-empty deque.

You'll have to protect all accesses (not just writes) to the deque through locks, or use a queue specifically designed for consumer-producer model in multi-threaded environment (such as Microsoft's unbounded_buffer).

Branko Dimitrijevic
  • 50,809
  • 10
  • 93
  • 167