Reputation: 10868
This scenario always occurs frequently: We have some threads, and a shared object, we need to make sure at any time only one thread can modify that object.
Well, the obvious solution is to use the lock the door-do the job-get out of there
idiom. In this situation, I always use POSIX mutexes. For example
pthread_mutex_lock(&this->messageRW); // lock the door
P_Message x = this->messageQueue.front(); // do the job
this->messageQueue.pop();
pthread_mutex_unlock(&this->messageRW); // get out of there
// somewhere else, in another thread
while (true) {
P_Message message;
solver->listener->recvMessage(message);
pthread_mutex_lock(&(solver->messageRW)); // lock the door
solver->messageQueue.push(message); // do the job
pthread_mutex_unlock(&(solver->messageRW)); // get out of there
sem_post(&solver->messageCount);
}
I use messageQueue
in so many places in code. So ended up with a lot of lock/unlock pairs which are inelegant. I think there should be a way to declare messageQueue
as an object that is supposed to be shared between threads, and then threading API can take care of lock/unlock. I can think of a wrapper class, or something similar. A POSIX-based solution is preferred though other API's (boost threads, or other libraries) are also acceptable.
What would you implement in a similar situation?
Update for future readers
I found this. Will be a part of C++14 I guess.
Upvotes: 0
Views: 724
Reputation: 153977
It's the message queue itself which should handle the locking (be atomic), not the calling code. And you need more than just a mutex, you need a condition as well to avoid race conditions. The standard idiom would be something like:
class ScopedLock // You should already have this one anyway
{
pthread_mutex_t& myOwned;
ScopedLock( ScopedLock const& );
ScopedLock& operator=( ScopedLock const& );
public:
ScopedLock( pthread_mutex_t& owned )
: myOwned( owned )
{
pthread_mutex_lock( &myOwned );
}
~ScopedLock()
{
pthread_mutex_unlock( &myOwned );
}
};
class MessageQueue
{
std::deque<Message> myQueue;
pthread_mutex_t myMutex;
pthread_cond_t myCond;
public:
MessageQueue()
{
pthread_mutex_init( &myMutex );
pthread_cond_init( &myCond );
}
void push( Message const& message )
{
ScopedLock( myMutex );
myQueue.push_back( message );
pthread_cond_broadcast( &myCond );
}
Message pop()
{
ScopedLock( myMutex );
while ( myQueue.empty() ) {
pthread_cond_wait( &myCond, &myMutex );
}
Message results = myQueue.front();
myQueue.pop_front();
return results;
}
};
This needs more error handling, but the basic structure is there.
Of course, if you can use C++11, you'ld be better off using the standard thread primitives. (Otherwise, I'd normally suggest Boost threads. But if you're already using Posix threads, you might want to wait to convert until you can use standard threads, rather than converting twice.) But you'll still need both a mutex and a condition.
Upvotes: 1
Reputation: 7919
You can use boost:scoped_lock
in this case. As soon as you go out of scope, it unlocks elegantly:
boost::mutex mMutex;//member mutex object defined somewhere
{ //scope operator start
boost::mutex::scoped_lock scopedLock(mMutex);
pthread_mutex_lock(); // scoped lock the door
P_Message x = this->messageQueue.front(); // do the job
this->messageQueue.pop();
} //scope operator end, unlock mutex
// somewhere else, in another thread
while (true) {
P_Message message;
solver->listener->recvMessage(message);
boost::mutex::scoped_lock scopedLock(mMutex); // scoped lock the door
solver->messageQueue.push(message); // do the job
sem_post(&solver->messageCount);
} //scope operator end, unlock mutex
Upvotes: 2
Reputation: 882028
For this I would either subclass (is-a) or include (has-a) the message queue class into another class which forced the use of mutexes.
That's functionally what other languages do such as with the Java synchronized
keyword - it modifieds the underlying object to be automatically protected.
Upvotes: 2