void WorkHandler::addWork(Work* w){
printf("WorkHandler::insertWork Thread, insertWork locking \n");
lock();
printf("WorkHandler::insertWork Locked, and inserting into queue \n");
m_workQueue.push(w);
signal();
unLock();
}
I followed a tutorial and I got this. I was wondering if it is ok to change the order of singal() and unLock() like this
void WorkHandler::addWork(Work* w){
printf("WorkHandler::insertWork Thread, insertWork locking \n");
lock();
printf("WorkHandler::insertWork Locked, and inserting into queue \n");
m_workQueue.push(w);
unLock();
signal();
}
If I can't do this, could you please give me details why I am not allowed to do this? Thanks in advance.
This article is really worth reading towards your question:
Signal with mutexed or not?
Assuming you use same mutex with conditional variable to make condition change to be atomic. There are two cases and you should know their behavior:
Pthreads are implemented with wait-morphing, that is, instead of waking up threads upon signaling, it just transfer threads on conditional variable to the attached mutex queue. So signal while locking is more preferable without too much performance impact.
For signaling before unlocking mutex, it may causes spurious wake-up. If your code is not well designed to handle predicate changes made by spurious wake-up, you should choose signal while holding the lock.
The answer to your question is "Yes". In fact, it's slightly preferable (as you've probably guessed) as it avoids the 'hurry up and wait' issue of waking up a thread to test a condition only to have it immediately block on the mutex it needs to acquire before testing the condition.
This answer is predicated on the guess that these things hold true:
lock
is a thin wrapper forpthread_mutex_lock
.unLock
is a thin wrapper forpthread_mutex_unlock
.signal
is a thing wrapper forpthread_cond_signal
.pthread_cond_wait
.First, there is no correctness issue here. Either order will work. Recall that whenever you use condition variables, you must loop on a predicate while waiting:
By signalling after the unlock, you don't introduce any correctness issues; the thread is still guaranteed to wake up, and the worst case is another wakeup comes first - at which point it sees the predicate becomes true and proceeds.
However, there are two possible performance issues that can come up.
Spurious wakeups. If you signal after the unlock, it's possible for another thread to issue another wakeup. Consider the following scenario:
As you can see, this can introduce a spurious wakeup, which might waste some CPU time.
Personally, I don't think it's worth worrying too much about it either way. You don't often know offhand whether your implementation supports moving waiters from the condition variable to the mutex wait queue, which is the only real criterion you could use to decide which to use.
My gut feeling would be that, if I had to choose, signalling after the unlock is marginally less likely to introduce an inefficiency, as the inefficiency requires a three-thread race, rather than a two-thread race for the "hurry up and wait" condition. However, this is not really worth worrying about, unless benchmarks show too much context switch overhead or something.