I've found the following topic (here) about pthreads but there are many good solutions.
I wanted to know if the following piece of code is valid and if so, why the same lock is used to call pthread_cond_wait as well as access it and then unlocked right away:
void suspendMe()
{
pthread_mutex_lock(&m_SuspendMutex);
pthread_cond_wait(&m_ResumeCond, &m_SuspendMutex);
pthread_mutex_unlock(&m_SuspendMutex);
}
Would it not be better to use 2 separate mutexes here, or is this the correct way to suspend a pthread??
Thanks in advance!
EDIT:
Awesome replies, thanks all. Just one more related question. Since I want to resume a thread separately in another function, would this be more proper in resuming it?
void suspendMe()
{
pthread_mutex_lock(&m_SuspendMutex);
pthread_cond_wait(&m_ResumeCond, &m_SuspendMutex);
}
void resumeMe()
{
pthread_cond_signal(&m_ResumeCond);
pthread_mutex_unlock(&m_SuspendMutex);
}
Thanks again everyone!! :~)
thread_state = -1; // signal to the thread to stop // maybe use pthread_exit(0) to exit main. // this will leave the threads running until they have finished tidy etc.
POSIX Threads, commonly known as pthreads, is an execution model that exists independently from a language, as well as a parallel execution model. It allows a program to control multiple different flows of work that overlap in time.
Actually, this code isn't thread safe. The mutex isn't actually protecting anything, leaving the implied predicate vulnerable to race conditions.
Look at this code -- what is the mutex protecting? What protects the suspend/resume state?
void suspendMe()
{
pthread_mutex_lock(&m_SuspendMutex);
pthread_cond_wait(&m_ResumeCond, &m_SuspendMutex);
}
void resumeMe()
{
pthread_cond_signal(&m_ResumeCond);
pthread_mutex_unlock(&m_SuspendMutex);
}
This is correct:
void suspendMe()
{ // tell the thread to suspend
pthread_mutex_lock(&m_SuspendMutex);
m_SuspendFlag = 1;
pthread_mutex_unlock(&m_SuspendMutex);
}
void resumeMe()
{ // tell the thread to resume
pthread_mutex_lock(&m_SuspendMutex);
m_SuspendFlag = 0;
phtread_cond_broadcast(&m_ResumeCond);
pthread_mutex_unlock(&m_SuspendMutex);
}
void checkSuspend()
{ // if suspended, suspend until resumed
pthread_mutex_lock(&m_SuspendMutex);
while (m_SuspendFlag != 0) pthread_cond_wait(&m_ResumeCond, &m_SuspendMutex);
pthread_mutex_unlock(&m_SuspendMutex);
}
The thread should call checkSuspend
at safe points where it can be suspended. Other threads can call suspendMe
and resumeMe
to suspend/resume the thread.
Notice that now the mutex protects the m_SuspendFlag
variable, ensuring that the thread is told to suspend, told to resume, and checks whether it should suspend or stay suspended under protection, making the code thread-safe.
Would it not be better to use 2 separate mutexes here, or is this the correct way to suspend a pthread??
Using two mutexes would defeat the entire point of condition variables. The whole mechanism by which they work is that you can check whether there is something you should wait for and then atomically wait for it without either holding the lock while you wait or having to release the lock and then wait. If you hold the lock while you wait, how can any other thread change the state? And if you release the lock and then wait, what happens if you miss the change in state?
By the way, it almost never makes sense to pause or resume a thread. If you feel like you need to pause a thread from the outside, that just indicates that you coded the thread to do something you didn't actually want it to do. Questions about pausing or resuming threads often indicate an incorrect mental model of thread programming. A thread might need to wait for something, but it shouldn't be "paused" from the outside because it should already know by its own coding when it shouldn't do some particular bit of work.
This is the correct way. pthread_cond_wait
unlocks m_SuspendMutex
, then waits on m_ResumeCond
and then locks m_SuspendMutex
again before returning.
The reason it works this way is because condition variables are used to signal a change of some state, and since this state is shared it must be locked while some thread is accessing it. E.g. consider implementing a queue of events:
T get_item()
{
pthread_mutex_lock(&mutex);
while(qu.empty())
pthread_cond_wait(&cond, &mutex);
T ret = qu.front();
qu.pop();
pthread_mutex_unlock(&mutex);
return ret; // we got an item from a queue
}
void add_item(T x)
{
pthread_mutex_lock(&mutex);
qu.push(x);
pthread_mutex_unlock(&mutex);
pthread_cond_signal(&cond);
}
Note that:
qu
is synchronized using the mutex
.mutex
in order to allow the other threads to add items to the queue, and when it have been changed we must lock the mutex
again to actually inspect the queue. This is exactly what pthread_cond_signal
does.If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With