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 the tutorial, and I realized that. I was wondering if it is okay to reorder 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 cannot do this, could you tell me the details, why am I not allowed to do this? Thanks in advance.

+12
c ++ pthreads
Jun 21 2018-11-11T00:
source share
3 answers

Firstly, there is no problem with correctness. Any order will work. Recall that whenever you use condition variables, you must loop around the predicate while waiting:

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

With a signal after unlocking, you do not enter any problems with the correctness; the flow is still guaranteed to wake up, and in the worst case, another awakening occurs - at this moment he sees that the predicate becomes true and continues.

However, two performance issues are possible:

  • "Hurry up and wait." Basically, if you signal that the lock is being held, the other thread should still wait until the mutexes are available. Many pthreads implementations, instead of waking up another thread, simply transfer it to the mutex wait queue, preserving an unnecessary wait loop → wait. In some cases, however, this is not implemented or not available, resulting in a potential false context switch or IPI.
  • Fake Awakenings. If you signal after unlocking, it is possible that another thread will issue another wake up. Consider the following scenario:

    • In thread A, the wait begins for items to be added to the thread queue.
    • Thread B inserts the item into the queue. After the queue is unlocked, but before it issues a signal, a context switch appears.
    • Thread C inserts the element into the queue and issues a cvar signal.
    • Thread A wakes up and processes both elements. Then he returns to waiting in line.
    • Thread B resumes and signals cvar.
    • Thread A wakes up and then immediately goes back to sleep because the queue is empty.

    As you can see, this can lead to a false awakening, which can lead to the cancellation of some processor time.

Personally, I don’t think it bothered too much about it. You do not often know, regardless of whether your implementation supports the transfer of waiters from a condition variable to the mutex wait queue, which is the only real criterion that you can use to decide what to use.

My gut feeling would be that if I had to choose, an alarm after unlocking would be less likely to introduce inefficiency, since inefficiency requires a three-threaded race, and not a two-threaded race for up and wait rush. However, actually it’s not worth worrying if the tests have too much redundant context switching or something else.

+18
Jun 21 2018-11-11T00:
source share

The answer to your question is yes. Actually, this is a little preferable (as you probably guessed), since it avoids the “rush and wait” problem of the thread waking up in order to check the condition, only that it immediately blocks the mutex that it should receive before testing the state.

This answer is based on the assumption that these things are true:

  • lock is a thin shell for pthread_mutex_lock .
  • unLock is a thin shell for pthread_mutex_unlock .
  • signal is a wrapper for pthread_cond_signal .
  • Mutex: your lock and unlock is what you give pthread_cond_wait .
+2
Jun 21 2018-11-11T00:
source share

This article is really worth reading for your question:

Signal with mutex or not?

Assuming you use the same mutex with a conditional variable to make the state change atomic. There are two cases, and you should know their behavior:

  • wait for a signal (conditional var) while holding the mutex. As a result, threads join the var conditional queue and then go into sleep mode.
  • but without a mutex. In this case, the thread will not sleep, but blocks it. (The mistake I made about this is that I thought she would sleep too. In this case, if the manufacturer’s signals and the context switch happen before it releases the mutex, then all the threads will wake up and find out that they cannot block the mutexes, go to sleep forever. This is wrong because they will not sleep, but wait and block).

Pthreads are implemented using wait-morphing , that is, instead of waking up threads when transmitting signals, it simply transfers the threads of the conditional variable to the attached mutex queue. Thus, a blocking signal is more preferable without significant performance impact.

For alarms, before unlocking the mutex, this may cause a false awakening. If your code is poorly designed to handle predicate changes caused by a false wakeup, you must select a signal while holding the lock.

+1
Apr 14 '17 at 22:12
source share



All Articles