[Linux] Thread synchronization

Before introducing thread synchronization, we need to understand the related concepts of deadlock.
Deadlock: Each process in a group of processes occupies resources that will not be released, but they are in a permanent waiting state due to mutual application for resources that will not be released by other processes.
Both threads and processes may deadlock

Four necessary conditions for deadlock
  • Mutually exclusive: each resource can only be used by one execution flow at a time
  • Request and hold conditions: When an execution flow is blocked by requesting resources, keep the acquired resources
  • Non-deprivation conditions: the resources obtained by an execution flow cannot be forcibly deprived before being used up
  • Circulation waiting condition: A kind of cyclic waiting resource relationship is formed between several execution streams.
Ways to avoid deadlock
  • 2, 3, 4 of the four necessary conditions for destroying deadlock
  • The order of locking is consistent, that is, synchronization
  • Avoid scenarios where the lock is not released
  • One-time resource allocation

Thread synchronization

In the previous article, we talked about the mutual exclusion of Linux threads. The lock mechanism is used to ensure the safety of threads. In this blog, we will explain to you the synchronization mechanism under Linux. The program is abnormal due to timing issues, which we call the competition state . In order to avoid a race condition, we need to allow threads to access critical resources in a specific order under the premise of ensuring data security, which is called synchronization .
In the OS, we provide a mutual exclusion mechanism to ensure data security, and a synchronization mechanism to ensure reasonableness.

To ensure the rationality and efficiency of thread synchronization, we need to use condition variables . When a thread accesses a variable exclusively, it may find that it cannot do anything before other threads change state. For example, when a thread accesses the queue and finds that the queue is empty, it can only wait until another thread adds a node to the queue. In this case, condition variables are needed. Let's take a look at the functions related to condition variables. These functions are really like mutual exclusion locks.

  1. Initialize condition variables: generally define and initialize global variables

int pthread_cond_init(pthread_cond_t *restrict cond,const pthread_condattr_t *restrict attr);
//Parameter one: the condition variable to be initialized. Parameter two: generally set to NULL

  1. destroy

int pthread_cond_destroy(pthread_cond_t *cond)

  1. Wait for the condition to be met in the condition queue

int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex);
//The first parameter is the condition variable to be waited for, and the second parameter is a mutex lock. The purpose of this lock will be explained later

  1. Wake up waiting

int pthread_cond_broadcast(pthread_cond_t *cond);//Wake up all condition variables, shock group problems
int pthread_cond_signal(pthread_cond_t *cond); //Wake up a thread

Examples of using condition variables:

The signal function in this program r2 wakes up the r1 function every 1 second, so the activity is printed every second.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
pthread_cond_t cond;
pthread_mutex_t mutex;
void *r1( void *arg )
{
	while ( 1 ){
	pthread_cond_wait(&cond, &mutex);
	printf("活动\n");
	}
} 
void *r2(void *arg )
{
	while ( 1 ) {
	pthread_cond_signal(&cond);
	sleep(1);
	}
}
 int main( void )
{
	pthread_t t1, t2;
	pthread_cond_init(&cond, NULL);
	pthread_mutex_init(&mutex, NULL);
	
	pthread_create(&t1, NULL, r1, NULL);
	pthread_create(&t2, NULL, r2, NULL);
	pthread_join(t1, NULL);
	pthread_join(t2, NULL);
	
	pthread_mutex_destroy(&mutex);
	pthread_cond_destroy(&cond);
} 

Insert picture description here

Why do pthread_cond_wait function parameters need to pass a mutex lock:
pthread_mutex_lock(&mutex);
//进入临界区访问临界资源
while (condition_is_false) {
	pthread_cond_wait(&cond);
}
 pthread_mutex_unlock(&mutex);

  • From the above code, we find that when a thread (perhaps a reader thread) enters the critical area, it is found that the critical resource does not have the conditions for reading at this time, so another thread is needed to write to the critical resource, but because we are now The reader thread holds the lock in the hand, so the writer thread cannot get the lock and cannot enter the critical section. To solve this problem pthread_cond_wait function will be performed while waiting to lock automatically released, then hang. It is worth noting that the two steps of releasing the lock and suspending waiting are combined into an atomic operation . When the condition is met and awakened, the reader thread also obtains the mutex lock at the same time.

  • So can releasing locks and waiting are not atomic operations?
    Let us first look at the following code:

pthread_mutex_lock(&mutex);
while (condition_is_false) {
	pthread_mutex_unlock(&mutex);
	//解锁之后,等待之前,条件可能已经满足,信号已经发出,但是该信号可能被错过
	pthread_cond_wait(&cond);
	pthread_mutex_lock(&mutex);
} 
pthread_mutex_unlock(&mutex);

Because unlocking and waiting are not atomic operations. After calling unlock and before pthread_cond_wait, if other threads have already acquired the mutex, abandoned the condition and sent the signal, then pthread_cond_wait will miss the signal, which may cause the thread to be blocked forever in this pthread_cond_wait. So unlocking and waiting must be an atomic operation.

Condition variable usage specification
  1. Waiting condition is thread waiting
pthread_mutex_lock(&mutex);
while(条件为假)
	pthread_cond_wait(cond,&mutex);
	修改条件
	pthread_mutex_unlock(&mutex);
	
  1. Signal the condition to wake up
pthread_mutex_lock(&mutex);
设置条件为真
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex);

In the next blog, we will use condition variables to implement the classic producer-consumer model.

Guess you like

Origin blog.csdn.net/ly_6699/article/details/98185290