Operating System Learning Improvement - Thread Synchronization

Mutex (mutex lock)

The working principle of the mutex: when a thread accesses a critical resource, it locks the critical resource to prevent another thread from accessing the critical resource, and then unlocks it after use.

When the instructions of two threads are executed interleaved, it will cause the problem of thread synchronization. Mutexes can be guaranteed to be executed sequentially, first executing the relevant instructions of one thread, and then executing the relevant instructions of the other thread. (atomicity)

Atomicity: A sequence of operations cannot be interrupted, either all of them are executed, or all of them are not executed.

Mutexes are the easiest way to synchronize threads.
Mutex, a variable in one of two states: unlocked and locked. The two states guarantee serialization of resource access.

The operating system directly provides the mutex API, and developers can directly use the API to complete the locking and unlocking of resources. The API of C is: pthread_mutex_t.

// Mutex example
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <vector>
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

//critical resource
int num=0;
//producer
void *producer(void*){
	int times = 100000000;
	while(time--){
		pthread_mutex_lock(&mutex);
		num+=1;
		pthread_mutex_unlock(&mutex);
	}
}
//consumer
void *consumer(void*){
	int times = 100000000;
	while(time--){
		pthread_mutex_lock(&mutex);
		num-=1;
		pthread_mutex_unlock(&mutex);
	}
}
int main(){
	printf("start in main function.");
	pthread_t thread1,thread2;
	pthread_create(&thread1,NULL,&producer,NULL);
	pthread_create(&thread2,NULL,&consumer,NULL);
	pthread_join(thread1,NULL);
	pthread_join(thread2,NULL);
	printf("print in main function:num= %d\n",num);
	return 0;
}

spin lock

The working principle of the spin lock: the same as the mutex, it is also locked when the critical resource is used, and then unlocked and released after completion to ensure serial access to the critical resource.

A spinlock is also a variable for multithreading synchronization.
A thread using a spinlock repeatedly checks to see if the lock variable is available.
The spin lock will not give up the CPU, it is a busy wait state.
A spinlock is a lock that waits in an infinite loop for the lock to be released.

Spinlocks avoid the overhead of process or thread context switching.
Spinlocks are used in many places within the operating system.
Spinlocks are not suitable for use on single-core CPU s.

The API of C is: pthread_spinlock_t

// Example of spin lock
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <vector>
pthread_spinlock_t spin_lock;

//critical resource
int num=0;
//producer
void *producer(void*){
	int times = 100000000;
	while(time--){
		pthread_spin_lock(&spin_lock);
		num+=1;
		pthread_spin_unlock(&spin_lock);
	}
}
//consumer
void *consumer(void*){
	int times = 100000000;
	while(time--){
		pthread_spin_lock(&spin_lock);
		num-=1;
		pthread_spin_unlock(&spin_lock);
	}
}
int main(){
	printf("start in main function.");
	pthread_spin_init(&spin_lock,0)
	pthread_t thread1,thread2;
	pthread_create(&thread1,NULL,&producer,NULL);
	pthread_create(&thread2,NULL,&consumer,NULL);
	pthread_join(thread1,NULL);
	pthread_join(thread2,NULL);
	printf("print in main function:num= %d\n",num);
	return 0;
}

Read-write lock

When a thread reads more and writes less to a critical resource (for example, a historical order database is generally only viewed but not changed), the value of the critical resource will not be changed when reading, and the use of read-write locks can improve efficiency.

A read-write lock is a special kind of spin lock.
Allows multiple readers to access resources at the same time to improve read performance.
Read and write are mutually exclusive, read and read are not mutually exclusive.

The API of C is: pthread_rwlock_t

// Example of a read-write lock
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <vector>
pthread_rwlock_t rwlock=PTHREAD_RWLOCK_INITIALIZER;

//critical resource
int num=0;
//read operation
void *reader(void*){
	int times = 100000000;
	while(time--){
		pthread_rwlock_rdlock(&rwlock);
		if(time%1000==0){
			usleep(10)
		}
		pthread_rwlock_unlock(&rwlock);
	}
}
//write operation
void *writer(void*){
	int times = 100000000;
	while(time--){
		pthread_rwlock_wdlock(&rwlock);
		num-=1;
		pthread_rwlock_unlock(&rwlock);
	}
}
int main(){
	printf("start in main function.");

	pthread_t thread1,thread2,thread3;
	pthread_create(&thread1,NULL,&reader,NULL);
	pthread_create(&thread2,NULL,&reader,NULL);
	pthread_create(&thread3,NULL,&writer,NULL);
	pthread_join(thread1,NULL);
	pthread_join(thread2,NULL);
	pthread_join(thread3,NULL);
	printf("print in main function:num= %d\n",num);
	return 0;
}

condition variable

Condition variables are a relatively complex method of thread synchronization.
Condition variables allow threads to sleep until a certain condition is met.
When the condition is met, the thread can be signaled to wake up.

In the producer-consumer model,
When the buffer is less than or equal to 0, consumers are not allowed to consume, and consumers must wait;
When the buffer is full, the producer is not allowed to produce to the buffer, and the producer must wait.
When a producer produces a product, wake up consumers that may be waiting;
When a consumer consumes a product, wake up possibly waiting producers.
The condition variable is to realize the wake-up operation

The API of C is: pthread_cond_t, which needs to be used with mutex.

// example of condition variable
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <vector>
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

//critical resource
int MAX_BUF=100;
int num=0;
//producer
void *producer(void*){
	while(true){
	    pthread_mutex_lock(&mutex);
		while(num>=MAX_BUF){
			//wait
			pthread_cond_wait(&cond,&mutex);
			printf("The buffer is full, waiting for consumers to consume...\n");
		}
		num+=1;
		printf("To produce a product, the current product quantity is:%d\n",num);
		sleep(1);
		pthread_cond_signal(&cond);
		printf("Notify consumers...\n");
		pthread_mutex_unlock(&mutex);
	}
}
//consumer
void *consumer(void*){
	while(true){
		while(num<=0){
			//wait
			pthread_cond_wait(&cond,&mutex);
			printf("The buffer is empty, waiting for the producer to produce...\n");
		}
		num-=1;
		printf("Consuming a product, the current product quantity is:%d\n",num);
		sleep(1);
		pthread_cond_signal(&cond);
		printf("Notify producer...\n");
		pthread_mutex_unlock(&mutex);
	}
}
int main(){
	printf("start in main function.");
	pthread_t thread1,thread2;
	pthread_create(&thread1,NULL,&producer,NULL);
	pthread_create(&thread2,NULL,&consumer,NULL);
	pthread_join(thread1,NULL);
	pthread_join(thread2,NULL);
	printf("print in main function:num= %d\n",num);
	return 0;
}

Summary of thread synchronization methods

Mutexes, spin locks, read-write locks

condition variable

Tags: Multithreading Operating System

Posted by elflacodepr on Mon, 30 May 2022 03:05:00 +0530