开发者

signal and unlock order

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 a开发者_StackOverflow社区nd 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.


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:

pthread_mutex_lock(mutex);
while (!predicate)
  pthread_cond_wait(cvar);
pthread_mutex_unlock(mutex);

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.

  • "Hurry up and wait". Basically, if you signal while the lock is held, the other thread still needs to wait until the mutex is available. Many pthreads implementations will, instead of waking up the other thread, simply move it to the wait queue of the mutex, saving an unnecessary wakeup->wait cycle. In some cases, however this is unimplemented or unavailable, leading to a potential spurious context switch or IPI.
  • Spurious wakeups. If you signal after the unlock, it's possible for another thread to issue another wakeup. Consider the following scenario:

    1. Thread A starts waiting for items to be added to a threadsafe queue.
    2. Thread B inserts an item on the queue. After unlocking the queue, but before it issues the signal, a context switch occurs.
    3. Thread C inserts an item on the queue, and issues the cvar signal.
    4. Thread A wakes up, and processes both items. It then goes back to waiting on the queue.
    5. Thread B resumes, and signals the cvar.
    6. Thread A wakes up, then immediately goes back to sleep, because the queue is empty.

    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.


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:

  1. wait on signal (conditional var) while holding the mutex. The result is to let thread join the conditional var's queue and then go to sleep.
  2. signaled but without mutex. For this case, the thread won't sleep but block on it. (A mistake I made on this is that I thought it will sleep too. In this case, if producer signals and context switch happens right before it releases mutex, then all threads will wake up and know they can't lock the mutex, go to sleep forever. This is wrong because they won't sleep but wait and block).

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 for pthread_mutex_lock.
  • unLock is a thin wrapper for pthread_mutex_unlock.
  • signal is a thing wrapper for pthread_cond_signal.
  • The mutex your locking and unlocking is the one that your giving to pthread_cond_wait.
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜