1. Thread mutual exclusion mode
1. What is a synchronous mutual exclusion? Why deal with synchronized mutexes?
Synchronization mutual exclusion is to make threads process tasks in a sequential order, in order to prevent thread resources from being preempted.
2. What are the ways to deal with synchronous mutual exclusion?
Semaphore -> Process (shared memory + semaphore used together) — one of the process IPC
Named semaphore -> process (shared memory + named semaphore used together)
unnamed semaphore -> thread
mutex -> thread
read-write lock -> thread
Condition variable -> thread (mutex + condition variable used together)
2. Basic concepts
The logic of the POSIX semaphore is exactly the same as that of the semaphore element in the IPC semaphore group, but the operation of the POSIX semaphore is easier and the interface is easier to use. It is widely used in multi-process and multi-thread.
POSIX semaphores are divided into two types:
- POSIX anonymous semaphores
- Usually used between threads
- Only exists in memory, not visible in the file system
- POSIX named semaphores
- Usually used between processes
- It exists in the file system /dev/shm and can be operated by different processes
3. Well-known semaphore
POSIX
named semaphore is mainly used to synchronize mutual exclusion between multiple processes, its P/V
operation is the same as the anonymous version, its biggest feature is that it exists in the file system /dev /shm, which can be opened by any privileged process in the system
The function interface description of the well-known semaphore is as follows:
sem_t *sem; 1) Create and open a well-known semaphore -> sem_open() #include <fcntl.h> /* For O_* constants */ #include <sys/stat.h> /* For mode constants */ #include <semaphore.h> sem_t *sem_open(const char *name, int oflag); sem_t *sem_open(const char *name, int oflag, mode_t mode, unsigned int value); Function role: Initialize and open a named semaphore parameter: name: The name of the famous semaphore, which must start with "/", such as "/sem_test or /my_sem" -> exist in the /dev/shm directory oflag: O_CREAT -> create if does not exist O_CREAT|O_EXCL -> Create if it does not exist -> Report an error if it exists mode: octal permission, for example: 0666 value: the starting value of the well-known semaphore ---> 0 Notice: If there is an option O_CREAT in oflag, the mode and value must be filled. If the famous semaphore already exists, but you write O_CREAT again, then the mode and value you fill in later will be ignored. return value: success: the address of the well-known semaphore Failed: SEM_FAILED -> NULL / (sem_t *)-1 sem_t* sem = sem_open(SEM_NAME,O_CREAT,0777,0); if(sem == SEM_FAILED) { printf("sem_open fail\\ "); return -1; } 2) P operation of well-known semaphore P operation: resource number - 1 operation -> sem_wait() -> man 3 sem_wait #include <semaphore.h> int sem_wait(sem_t *sem); -> If it cannot be reduced by 1, this function will block parameter: sem: the address of the well-known semaphore return value: Success: 0 -> resource count can -1 Failed: -1 for example: If the current value is 2, then sem_wait() will return immediately and set the value to 1. If the current value is 1, then sem_wait() will return immediately and set the value to 0. If the current value is 0, then sem_wait() will block until the value of the named semaphore is not 0. 3) V operation of well-known semaphore V operation: number of resources + 1 operation -> sem_post() -> man 3 sem_post #include <semaphore.h> int sem_post(sem_t *sem); -> must be able to + 1, absolutely will not block parameter: sem: the address of the well-known semaphore return value: Success: 0 -> number of resources can + 1 Failed: -1 4) Close the well-known semaphore. -> sem_close() -> man 3 sem_close #include <semaphore.h> int sem_close(sem_t *sem); parameter: sem: the address of the well-known semaphore return value: Success: 0 Failed: -1 5) Delete the well-known semaphore. -> sem_unlink() -> man 3 sem_unlink #include <semaphore.h> int sem_unlink(const char *name); (named semaphore name) parameter: name: the name of the well-known semaphore return value: Success: 0 Failed: -1 When compiling a famous semaphore, you need to add a thread library to compile
Well-known semaphores are tested as follows:
#include<stdio.h> #include <sys/ipc.h> #include <sys/shm.h> #include <sys/types.h> #include <fcntl.h> /* For O_* constants */ #include <sys/stat.h> /* For mode constants */ #include <semaphore.h> #include <string.h> #define SEM_NAME "/semname1" //Famous semaphore _wr.c int main() { //1. Get the key value key_t key = ftok(".",10); //2. Obtain the ID number of the shared memory according to the key value int shmid = shmget(key,1024,IPC_CREAT|0666); //3. Map the shared memory to a certain area of the virtual memory space of the process according to the ID number char*shm_p = shmat(shmid,NULL,0); if(shm_p == (void*)-1) { perror("shmat error"); return -1; } //4. Create and open a well-known semaphore sem_t *nameSem = sem_open(SEM_NAME,O_CREAT,0777,0); if(nameSem == SEM_FAILED ) { printf("sem_open error\\ "); return -1; } //The shm_p mapped out at this time is the shared memory of the two processes while(1) { // Get data from the keyboard and store it in the shared memory shm_p scanf("%s",shm_p); //V operation of well-known semaphore data + 1 sem_post(nameSem); //Exit condition, here should be noted that strncmp should be used to specify the number of bytes if(strncmp(shm_p,"exit",4) == 0) break; } //4. When it is no longer used, release the mapping relationship shmdt(shm_p); //5. When no process needs to use this shared memory anymore, delete it shmctl(shmid, IPC_RMID, NULL); //6. Close the well-known semaphore. sem_close(nameSem); //7. Delete the famous semaphore sem_unlink(SEM_NAME); return 0; } //Famous semaphore_rd.c int main() { //1. Get the key value key_t key = ftok(".",10); //2. Obtain the ID number of the shared memory according to the key value int shmid = shmget(key,1024,IPC_CREAT|0666); //3. Map the shared memory to a certain area of the virtual memory space of the process according to the ID number char*shm_p = shmat(shmid,NULL,0); if(shm_p == (void*)-1) { perror("shmat error"); return -1; } //4. Create and open a well-known semaphore sem_t *nameSem = sem_open(SEM_NAME,O_CREAT,0777,0); if(nameSem == SEM_FAILED ) { printf("sem_open error\\ "); return -1; } //The shm_p mapped out at this time is the shared memory of the two processes while(1) { //P operation data of well-known semaphore -1 sem_wait(nameSem); // read data from shared memory printf("recv:%s\\ ",shm_p); //Exit condition, here should be noted that strncmp should be used to specify the number of bytes if(strncmp(shm_p,"exit",4) == 0) break; } return 0; }
4. Comparison between famous semaphore and semaphore
difference:
1) The semaphore function is complex, and the well-known semaphore function is simple.
2) The semaphore has space and data to operate; the famous semaphore only needs to operate on the data.
3) The thread library is not required when compiling the semaphore; the thread library needs to be linked when compiling the famous semaphore.
Same point:
1) Both act on communication between processes
2) There are P/V operations; P is minus 1 operation, V is plus 1 operation
5. Unnamed semaphore
It is generally used for mutual exclusion between threads. Since it is an unnamed semaphore, it has no name and cannot be opened with sem_open
The function interface description of the unnamed semaphore is as follows:
1) Define an unnamed semaphore (data type: sem_t) ---> global variable sem_t sem; -> The unnamed semaphore is not a file, but a variable, so the sem_open() function cannot be used 2) Initialize unnamed semaphore -> sem_init() -> man 3 sem_init #include <semaphore.h> int sem_init(sem_t *sem, int pshared, unsigned int value); parameter: sem: the address of the variable of the unnamed semaphore pshared: 0 -> act between threads -> only consider this case Non-0 -> act between processes value: the starting value of the unnamed semaphore ---> 1 return value: Success: 0 Failed: -1 sem_init( & g_sem,0,1); 3) Destroy the unnamed semaphore. -> sem_destroy() -> man 3 sem_destroy #include <semaphore.h> int sem_destroy(sem_t *sem); parameter: sem: the address of the unnamed semaphore return value: Success: 0 Failed: -1 The PV operation function is the same as the well-known semaphore
The unnamed semaphore test code is as follows:
#include<stdio.h> #include <pthread.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> #include <string.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <semaphore.h> //1. Define an unnamed semaphore variable sem_t g_sem; int g_val = 0; //Routine function of thread 1 void* start_routine1(void *arg) { printf("%s start ...\\ ",__FUNCTION__); //Before accessing shared resources, check to see if other threads are using them //P operation of unnamed semaphore //That is to say, can the -1 operation be performed? If so, go down //If not, block here sem_wait( & g_sem); g_val = 200; sleep(1); printf("start_routine1 g_val:%d\\ ",g_val); //When this thread no longer uses this shared resource, the unnamed semaphore performs V operation sem_post( & g_sem); printf("%s end ...\\ ",__FUNCTION__); } //Routine function of thread 2 void* start_routine2(void *arg) { printf("%s start ...\\ ",__FUNCTION__); sem_wait( & g_sem); sleep(1); g_val = 400; printf("start_routine2 g_val:%d\\ ",g_val); //When this thread no longer uses this shared resource, the unnamed semaphore performs V operation sem_post( & g_sem); printf("%s end ...\\ ",__FUNCTION__); } //Routine function of thread 3 void* start_routine3(void *arg) { printf("%s start ...\\ ",__FUNCTION__); sem_wait( & g_sem); sleep(1); g_val = 600; printf("start_routine3 g_val:%d\\ ",g_val); //When this thread no longer uses this shared resource, the unnamed semaphore performs V operation sem_post( & g_sem); printf("%s end ...\\ ",__FUNCTION__); } int main() { //2. Initialize the unnamed semaphore ---sem_init sem_init( & g_sem,0,1); //Create a child thread pthread_t thread1; pthread_create( & thread1, NULL, start_routine1, NULL); pthread_t thread2; pthread_create( & thread2, NULL, start_routine2, NULL); pthread_t thread3; pthread_create( & thread3, NULL, start_routine3, NULL); sem_wait( & g_sem); g_val = 1000; printf("main g_val:%d\\ ",g_val); //When this thread no longer uses this shared resource, the unnamed semaphore performs V operation sem_post( & g_sem); //Block and wait for the child thread to exit, recycle resources pthread_join(thread1, NULL); pthread_join(thread2, NULL); pthread_join(thread3, NULL); //destroy unnamed semaphore sem_destroy( & g_sem); }
The knowledge points of the article match the official knowledge files, and you can further learn relevant knowledge