dummy
dummy

Reputation: 265

How to Create Thread-Safe Buffers / POD?

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.

Upvotes: 14

Views: 3918

Answers (4)

phantom
phantom

Reputation: 3342

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.

Upvotes: 11

Amxx
Amxx

Reputation: 3070

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 !

Upvotes: 2

Schultz9999
Schultz9999

Reputation: 8926

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

Upvotes: 3

lcs
lcs

Reputation: 4245

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.

Upvotes: 5

Related Questions