Detailed explanation of pthread_cond_t under linux

At a glance

 


Purpose of this article

 
  First of all, the focus of this article is not how to use conditional variables. Here I first list   such a paragraph about the pthread_cond_wait function in apue :

  • "The caller passes the locked mutex to the function , and the function automatically puts the calling thread on the list of threads waiting for the condition, **unlocks the mutex.** This turns off the condition check and the thread goes to sleep Waiting for the condition to change the time channel between these two operations, so that the thread does not miss any changes in the condition. When pthread_cond_wait returns, the mutex is locked again. "

  The amount of information in this passage is very large, and the operation of the mutex can be understood as the following three points:

  1. Before calling pthread_cond_wait, you need to lock the mutex mutex before passing &mutex to the pthread_cond_wait function
  2. Inside the pthread_cond_wait function, the incoming mutex will be unlocked first
  3. When the waiting condition comes, the pthread_cond_wait function will lock the incoming mutex before returning

  I saw all kinds of questions at the time. Why was it locked before it was passed in, why it was released after it was passed in, and why was it locked again when it returned?

This article explains these three issues in detail. But before that, we need to understand why there are conditional variables. That is, the role of condition variables.


Why condition variables are needed

 
  If there is no conditional variable, then we wait for a condition to be met, then we will be the following model:
Insert picture description here
  first lock and enter the critical area to check whether the condition is met, if not satisfied, unlock and leave the critical area, sleep for a period of time and then continue to cycle judgment. In this case, if you just leave the critical section and the condition becomes satisfied, then the thread must wait a period of time to re-enter the critical section to know that the condition is satisfied (if the condition remains satisfied during this period), if this The condition becomes unsatisfied for a short period of time, then this thread will continue to loop judgment. Constantly locking and unlocking (which will affect other threads using the same lock), the condition is not met in the first time. This model is time-consuming and expensive.

  Therefore, the condition variable is generated just to not cyclically lock and unlock, and to receive the notification that the condition is satisfied the first time.


Three questions

 
  To answer those three questions, you first need to understand the coordination of waiting and awakening.

  The picture below is a correction I made after referring to other people's pictures (the original picture is wrong). In fact, this figure can explain the three questions: why pthread_cond_wait is locked before it is passed in, why it is unlocked after it is passed in, and why it is locked before returning. But let me explain in detail.
Insert picture description here
  There is a key point in the figure, which is to judge whether the condition is satisfied. It is before calling pthread_cond_wait and after locking, that is, pthread_cond_wait does not have the ability to judge conditions, and we need to write judgment statements externally.

  1. When the condition is not met, pthread_cond_wait will be entered
  2. Enter pthread_cond_wait to unlock and block immediately
  3. pthread_cond_signal wakes up the process blocked in pthread_cond_wait

You can combine the following code to make it clearer.

The following pseudo code for the usual usage of pthread_cond_wait and pthread_cond_signal (condition is: Is value greater than 0):

	lock(&mutex);
	while(value<=0)//需要value>0所以 value<=0就条件不满足
	{
		pthread_cond_wait(&cond,&mutex);
	}
	unlock(&mutex);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
	lock(&mutex);
	if(value==0)
	{
		value++;
	}
	if(value>0)
	{
		pthread_cond_signal(&cond);
	}
	unlock(&mutex);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

After clarifying this basic process, the three questions can be explained.


Lock mutex before incoming

Insert picture description here
  To make it easier for everyone to watch, I will post the above picture again for the explanation of each question.

Locking mutex before passing in is to ensure that the condition is not changed before the thread judges from the condition to enter pthread_cond_wait.

  If there is no lock before incoming. There will be such a situation: after thread A judges that the condition is not met, before calling pthread_cond_wait, because A is sleeping, or because of the execution order and speed of multiple threads under multithreading, thread B changes the condition so that the condition is satisfied. But at this time, thread A has not called pthread_cond_wait. After thread A starts to call pthread_cond_wait again, although the condition is met, it cannot receive the wake-up of pthread_cond_signal, and it keeps blocking.


Unlock mutex after incoming

Insert picture description here
Unlock after incoming is for the condition to be changed

  The unlock after passing in is because the part that calls pthread_cond_signal needs to be locked and changed before calling pthread_cond_signal. (Change the condition and wait for the condition to be satisfied are both for the competition of the condition, so the two threads calling pthread_cond_wait and calling pthread_cond_signal need the same lock)

  If the mutex is not unlocked in pthread_cond_wait, after calling pthread_cond_wait, other threads cannot change the condition, and the condition will not be satisfied.

Lock mutex again before returning

Insert picture description here

  1. Locking the mutex again before returning is to ensure that the thread is not changed after returning from pthread_cond_wait to before the condition is judged again.
  2. Ensure that other statements that may be required between pthread_cond_signal and unlock mutex can be executed

  For 1, the reason here is similar to the reason for locking the mutex before passing pthread_cond_wait. If it is not locked, after thread A calls pthread_cond_wait, the condition is met, thread A is awakened, and returns from pthread_cond_wait. Thread B changes the condition at this time, making the condition not satisfied. Thread A does not know that the condition is changed again, or thinks that the condition is met, it may go wrong.

  For 2, as long as there are other statements that need to be executed between pthread_cond_signal and unlocking the mutex, since the mutex has been locked by this thread at this time and has not been unlocked, the behavior of the thread that called pthread_cond_wait before pthread_cond_wait returns before the lock mutex is Will block until the statement after pthread_cond_signal is executed and unlocked, pthread_cond_wait will not return.

By the way, since pthread_cond_wait returns to lock again, pthread_cond_signal is not necessarily placed in the middle of lock() and unlock().

Two ways to write pthread_cond_signal

lock(&mutex);
//一些操作
pthread_cond_signal(&cond);
//一些操作
unlock(&mutex);
  • 1
  • 2
  • 3
  • 4
  • 5

  Disadvantages: In the implementation of some threads, the waiting thread will wake up from the kernel (due to cond_signal) and return to the user space, and then pthread_cond_wait needs to be locked before returning, but it is found that the lock has not been released and returns to the kernel space. Once there will be performance issues.
  But in LinuxThreads or NPTL, there is no such problem, because in Linux threads, there are two queues, cond_wait queue and mutex_lock queue, cond_signal just allows the thread to move from cond_wait queue to mutex_lock queue without returning to the user Space, there will be no performance loss. So it is no problem to use it in Linux.
 
 

lock(&mutex);
//一些操作
unlock(&mutex);
pthread_cond_signal(&cond);
  • 1
  • 2
  • 3
  • 4

Advantages: The potential performance loss mentioned before will not occur, because the lock has been released before the signal

Disadvantages: If process exchange occurs after unlock and before signal, another process (not the process waiting for the condition) gets the coveted lock and then adds the lock operation, then the lock will be taken back by others when it finally switches to the thread waiting for the condition Did not return, can only continue to wait.


Epilogue

  Condition variables should be used more to have a clearer understanding. Just looking at the API still doesn't work.

the above

Guess you like

Origin blog.csdn.net/star871016/article/details/109642644