14

My problem is quite common I suppose, but it drives me crazy:

I have a multi-threaded application with 5 threads. 4 of these threads do their job, like network communication and local file system access, and then all write their output to a data structure of this form:

struct Buffer {
  std::vector<std::string> lines;
  bool has_been_modified;
}

The 5th thread prints these buffer/structures to the screen:

Buffer buf1, buf2, buf3, buf4;

...

if ( buf1.has_been_modified || 
     buf2.has_been_modified || 
     buf3.has_been_modified || 
     buf4.has_been_modified )
{
  redraw_screen_from_buffers();
}

How do I protect the buffers from being overwritten while they are either being read from or written to?

I can't find a proper solution, although I think this has to be a quiet common problem.

Thanks.

CinchBlue
  • 6,046
  • 1
  • 27
  • 58
dummy
  • 265
  • 3
  • 10
  • 1
    use [mutexes](http://en.cppreference.com/w/cpp/thread/mutex) – Diego Apr 25 '15 at 21:43
  • Thanks for your reply. I'm not very experienced in multi threading. Could you please give a short example? – dummy Apr 25 '15 at 21:46
  • Is there any particular reason you were asking for a POD? (note the currently accepted answer does not describe a POD) –  Apr 26 '15 at 04:37
  • Yeah, I didn't want to build a big class around my `struct` with getters and setters. Also calling my `struct` a buffer didn't seem 100% right to me, so I added the POD. – dummy Apr 26 '15 at 17:26

4 Answers4

11

You should use a mutex. The mutex class is std::mutex. With C++11 you can use std::lock_guard<std::mutex> to encapsulate the mutex using RAII. So you would change your Buffer struct to

struct Buffer {
   std::vector<std::string> lines;
   bool has_been_modified;
   std::mutex mutex;
};

and whenever you read or write to the buffer or has_been_modified you would do

std::lock_guard<std::mutex> lockGuard(Buffer.mutex); //Do this for each buffer you want to access
... //Access buffer here

and the mutex will be automatically released by the lock_guard when it is destroyed.

You can read more about mutexes here.

phantom
  • 3,292
  • 13
  • 21
  • 1
    @user4832939 - while a mutex is what you're explicitly asking for, it seems like the situation that you're describing would be a valid use case for a condition variable. Check Schultz9999's answer for the details. – Daniel Kamil Kozar Apr 25 '15 at 22:12
  • Ahem: read or modify the buffer, or the has_been_modified bool too. Not just modify. (More specifically, you also need to lock around reads if it is possible that some other thread might be writing at the same time as the read) Look up the definition of what a race condition is. – Andre Kostur Apr 26 '15 at 06:46
5

You can use a mutex (or mutexes) around the buffers to ensure that they're not modified by multiple threads at the same time.

// Mutex shared between the multiple threads
std::mutex g_BufferMutex;

void redraw_screen_from_buffers()
{
   std::lock_guard<std::mutex> bufferLockGuard(g_BufferMutex);
   //redraw here after mutex has been locked.
}

Then your buffer modification code would have to lock the same mutex when the buffers are being modified.

void updateBuffer()
{
   std::lock_guard<std::mutex> bufferLockGuard(g_BufferMutex);
   // update here after mutex has been locked
}

This contains some mutex examples.

lcs
  • 4,227
  • 17
  • 36
  • bad, cause that means not more than one buffer modified at the time – Krab Apr 25 '15 at 21:45
  • @Krab Then use a mutex per buffer, and the `redraw_screen_from_buffers` function locks all the mutexes before redrawing. – lcs Apr 25 '15 at 21:48
3

What appears you want to accomplish is to have multiple threads/workers and one observer. The latter needs to do its job only when all workers are done/signal. If this is the case then check code in this SO q/a. std::condition_variable - Wait for several threads to notify observer

Community
  • 1
  • 1
Schultz9999
  • 8,717
  • 8
  • 48
  • 87
2

mutex are a very nice thing when trying to avoid dataraces, and I'm sure the answer posted by @Phantom will satisfy most people. However, one should know that this is not scalable to large systems.

By locking you are synchronising your threads. As only one at a time can be accessing the vector, on thread writting to the container will cause the other one to wait for it to finish ... with may be good for you but causes serious performance botleneck when high performance is needed.

The best solution would be to use a more complexe lock free structure. Unfortunatelly I don't think there is any standart lockfree structure in the STL. One exemple of lockfree queue is available here

Using such a structure, your 4 working threads would be able to enqueue messages to the container while the 5th one would dequeue them, without any dataraces

More on lockfree datastructure can be found here !

Amxx
  • 3,020
  • 2
  • 24
  • 45