1, Overview of mutual exclusion and synchronization
In a multitasking operating system, multiple tasks running at the same time may need to access and use the same resource
There are dependencies between multiple tasks, and the operation of one task depends on another task
Synchronization and mutual exclusion are used to solve these two problems
Mutual exclusion means that a public resource can only be used by one process or thread at the same time, and multiple processes or threads cannot access public resources at the same time.
Synchronization means that two or more processes or counties coordinate and operate in a certain order during operation. Synchronization is to add order on the basis of mutual exclusion
The methods of process and thread synchronization and mutual exclusion in POSIX standard are semaphore and mutual exclusion lock
2, Mutex
1. Concept
mutex is a simple locking method to control access to shared resources. mutex has only two states, lock and unlock.
You need to apply for mutex before accessing the resource. If the resource is in the unlock status, mutex will apply for and lock it immediately
If the resource is in lock status, the applicant will be blocked by default and wait for mutex to unlock
Unlocking can only be performed by the locker
2. Initialize mutex
Pthread for mutex_ mutex_ T data type means that it must be initialized before using a mutex
Statically allocated mutex:
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER
Dynamically allocated mutexes:
pthread_mutex_t mutex;
pthread_mutex_init(&mutex,NULL);
Pthread should be used when all threads using this mutex are no longer needed_ mutex_ Destroy destroy mutex
#include <pthread.h> int pthread_mutex_init(pthread_mutex_t &mutex,pthread_mutexattr_t *attr);
Function: initialize mutex
Parameter: mutex: mutex variable
attr: the attribute of the mutex. NULL is the default attribute
Return value: success 0 failure non-0
3. Mutex lock
#include<pthread.h> int pthread_mutex_lock(pthread_mutex_t *mutex);
Function: lock the mutex. If it is locked, the caller will block it until the mutex is unlocked
Parameter: address of mutex
Return value: success 0 failure non-0
#include <pthread.h> int pthread_mutex_trylock(pthread_mutex_t *mutex);
Function: try to lock the mutex. If it has been locked, the locking failure will be returned immediately
Parameter: address of mutex
Return value: success 0 failure non-0
4. Mutex unlock
#include <pthread.h> int pthread_mutex_unlock(pthread_mutex_t *mutex);
Function: Unlock the specified mutex
Return value: 0 or non-0
5. Destroy mutex
#include <pthread.h> int pthread_mutex_destroy(pthread_mutex_t *mutex);
Function: destroy the specified mutex
Return value: 0 or non-0
6. Examples
The following example shows the problem that two threads read common resources at the same time without adding a mutex
#include <stdio.h> #include <stdlib.h> #include <pthread.h> #include <unistd.h> int num = 2048; void * thread1_func(void * arg) { int readnum,subnum; printf("thread1 reading num......\n"); sleep(1); readnum = num; printf("thread1 num = %d\n",readnum); subnum = 2000; sleep(1); if(readnum >= subnum){ num = readnum - subnum; printf("thread1 success! num = %d\n",num); }else{ printf("thread1 error! num = %d\n",num); } } void * thread2_func(void * arg) { int readnum,subnum; printf("thread2 reading num......\n"); sleep(1); readnum = num; printf("thread2 num = %d\n",readnum); sleep(1); subnum = 2000; if(readnum >= subnum){ num = readnum - subnum; printf("thread2 success! num = %d\n",num); }else{ printf("thread2 error! num = %d\n",num); } } int main(){ printf("running main thread\n"); pthread_t thread1,thread2; int a = 123; if(pthread_create(&thread1,NULL,thread1_func,NULL)!=0){ perror("fail to pthread_create"); exit(1); } if(pthread_create(&thread2,NULL,thread2_func,NULL)!=0){ perror("fail to pthread_create"); exit(1); } pthread_join(thread1,NULL); pthread_join(thread2,NULL); return 0; }
The above example is modified by adding a mutex
#include <stdio.h> #include <stdlib.h> #include <pthread.h> #include <unistd.h> int num = 2048; //Because multiple threads use this mutex, the mutex is defined as global //If it is defined in the main function, the mutex needs to be passed through parameters, which is troublesome pthread_mutex_t mutex; void * thread1_func(void * arg) { int readnum,subnum; printf("thread1 reading num......\n"); pthread_mutex_lock(&mutex);//Lock out before using public resources sleep(1); readnum = num; printf("thread1 num = %d\n",readnum); subnum = 2000; sleep(1); if(readnum >= subnum){ num = readnum - subnum; printf("thread1 success! num = %d\n",num); }else{ printf("thread1 error! num = %d\n",num); } pthread_mutex_unlock(&mutex);//Unlock after using public resources } void * thread2_func(void * arg) { int readnum,subnum; printf("thread2 reading num......\n"); pthread_mutex_lock(&mutex);//Lock out before using public resources sleep(1); readnum = num; printf("thread2 num = %d\n",readnum); sleep(1); subnum = 2000; if(readnum >= subnum){ num = readnum - subnum; printf("thread2 success! num = %d\n",num); }else{ printf("thread2 error! num = %d\n",num); } pthread_mutex_unlock(&mutex);//Unlock after using public resources } int main(){ printf("running main thread\n"); pthread_t thread1,thread2; pthread_mutex_init(&mutex,NULL);//Mutex initialization if(pthread_create(&thread1,NULL,thread1_func,NULL)!=0){ perror("fail to pthread_create"); exit(1); } if(pthread_create(&thread2,NULL,thread2_func,NULL)!=0){ perror("fail to pthread_create"); exit(1); } pthread_join(thread1,NULL); pthread_join(thread2,NULL); pthread_mutex_destroy(&mutex);//Destroy mutex return 0; }
3, Semaphore (the following is based on the application of interprocess communication, and there are more other functions in interprocess communication)
1. General
Semaphores are widely used for synchronization and mutual exclusion between processes or threads. Semaphores are essentially a non negative integer counter, which is used to control access to public resources.
During programming, you can judge whether you have access to public resources according to the result of operating the semaphore value. When the semaphore value is greater than 0, you can access, otherwise it will be blocked.
Semaphore is also called PV operation. A p operation will reduce the sem value by one, and a V operation will increase the sem value by one. For P operation, if the semaphore sem value is less than or equal to 0, the P operation will be blocked. If the semaphore sem value is greater than 0, the P operation can be performed to reduce by one
Semaphores are mainly used for synchronization and mutual exclusion between processes or threads:
- If used for mutual exclusion, several processes (or threads) often set only one semaphore.
- If it is used for synchronous operation, multiple semaphores are often set and different initial values are arranged to realize the execution order between them.
Only one semaphore is required for mutual exclusion, regardless of several threads:
Program execution initialization semaphore (sem=1)
= = > thread 1 P operation (sem=0) = = > thread 1 execution = = > execution end V operation (sem=1)
= = > thread 2 P operation (sem=0) = = > thread 2 execution = = > execution end V operation (sem=1)
==>... The execution order of the above threads is random
Multiple semaphores are required for synchronization:
Program execution = = "initialization semaphore (sem1 = 1, Sem2 = 0)
= = > thread 1P operation sem1, sem1 becomes 0 = = > thread 1 execution = = > execution end V operation sem2
= = > thread 2P operation sem2, sem2 becomes 0 = = > thread 2 execution = = > execution end V operation sem1
2. Semaphore initialization
#include <semaphore.h> int sem_init(sem_t *sem,int pshared,unsigned int value);
Function: create and initialize semaphores
Parameter: sem: address of semaphore
pshared: optional values:
0: signal sharing between processes
Greater than 0: signals are shared between processes
Value: initial value of semaphore
Return value: 0 - 1
3. Semaphore P operation
#include <semaphore.h> int sem_wait(sem_t *sem);
Function: reduce the value of semaphore by one. If the value of semaphore is less than or equal to 0, it will be blocked
Parameter: address of semaphore
Return value: 0 - 1
#include <semaphore.h> int sem_try_trywait(sem_t *sem);
Function: reduce the semaphore value by one. If the semaphore value is less than or equal to 0, the semaphore operation fails and returns immediately
Return value: 0 - 1
4. Semaphore V operation
#include <semaphore.h> int sem_post(sem_t *sem);
Function: add the semaphore value to send a signal to wake up the waiting thread
Parameter: address of semaphore
Return value: 0 - 1
5. Gets the count value of the semaphore
#include <semaphore.h> int sem_getvalue(sem_t *sem,it *sval);
Function: obtain the semaphore value represented by sem and save it in sval
Parameter: sem: address of semaphore
sval: address to save semaphore value
Return value: 0 - 1
6. Destroy semaphore
#include <semaphore.h> int sem_destroy(sem_t *sem);
Function: destroy semaphore
Parameter: semaphore address
Return value: 0 - 1
7. Instance (synchronization)
Use two threads to output hello and world!, When semaphores are not used:
#include <stdio.h> #include <stdlib.h> #include <pthread.h> #include <unistd.h> void printer(char *str){ while(*str != '\0') { printf("%c",*str); fflush(stdout); str++; sleep(1); } } void * thread1_func(void * arg) { char *str = "hello"; printer(str); } void * thread2_func(void * arg) { char *str = "world"; printer(str); } int main(){ printf("running main thread\n"); pthread_t thread1,thread2; if(pthread_create(&thread1,NULL,thread1_func,NULL)!=0){ perror("fail to pthread_create"); exit(1); } if(pthread_create(&thread2,NULL,thread2_func,NULL)!=0){ perror("fail to pthread_create"); exit(1); } pthread_join(thread1,NULL); pthread_join(thread2,NULL); sleep(1); printf("\n"); return 0; }
After using semaphores:
#include <stdio.h> #include <stdlib.h> #include <pthread.h> #include <unistd.h> #include <semaphore.h> sem_t sem1,sem2; void printer(char *str){ while(*str != '\0') { printf("%c",*str); fflush(stdout); str++; sleep(1); } } void * thread1_func(void * arg) { char *str = "hello "; sem_wait(&sem1); printer(str); sem_post(&sem2); } void * thread2_func(void * arg) { char *str = "world"; sem_wait(&sem2); printer(str); sem_post(&sem1); } int main(){ printf("running main thread\n"); pthread_t thread1,thread2; sem_init(&sem1,0,1); sem_init(&sem2,0,0); if(pthread_create(&thread1,NULL,thread1_func,NULL)!=0){ perror("fail to pthread_create"); exit(1); } if(pthread_create(&thread2,NULL,thread2_func,NULL)!=0){ perror("fail to pthread_create"); exit(1); } pthread_join(thread1,NULL); pthread_join(thread2,NULL); sleep(1); printf("\n"); sem_destroy(&sem1); sem_destroy(&sem2); return 0; }