Experimental principle and content
Critical area management based on mutual exclusion
- Using the editor gedit 2_1.c, create a new 2_1.c source file, create two threads to complete the ticket booking operation concurrently, and enter the following example code:
#include <stdio.h> #include <pthread.h> #include <unistd.h> int ticketAmount = 2; //Global Variable void* ticketAgent(void* arg){ int t = ticketAmount; if (t > 0) { printf("One ticket sold!\n"); t--; }else{ printf("Ticket sold out!!\n"); } ticketAmount = t; pthread_exit(0); } int main(int argc, char const *argv[]) { pthread_t ticketAgent_tid[2]; //Create two threads through a loop based on the same thread function for (int i = 0; i < 2; ++i) { pthread_create(ticketAgent_tid+i, NULL, ticketAgent,NULL); } //The loop waits for all threads to end for (int i = 0; i < 2; ++i) { pthread_join(ticketAgent_tid[i],NULL); } printf("The left ticket is %d\n", ticketAmount); return 0; }
The two threads share a global variable ticketAmount as the ticketing terminal. After the two threads run, print the remaining ticket amount in the main thread, compile and run this code for many times, observe the results, explain the reasons, and think about the range of the critical area in the thread function.
Multiple runs 2_ After 1. C, the result output with the remaining votes of 1 appears, because two threads access the critical area at the same time.
(2) Mutex access to critical area based on mutex lock
For the critical area in the thread function, the mutex lock in the pthread library is used to realize the mutex access of the critical area.
#include <stdio.h> #include <pthread.h> #include <unistd.h> int ticketAmount = 2; //Global Variable pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; //Global lock void* ticketAgent(void* arg){ pthread_mutex_lock(&lock); int t = ticketAmount; if (t > 0) { printf("One ticket sold!\n"); t--; }else{ printf("Ticket sold out!!\n"); } ticketAmount = t; pthread_mutex_unlock(&lock); pthread_exit(0); } int main(int argc, char const *argv[]) { pthread_t ticketAgent_tid[2]; for (int i = 0; i < 2; ++i) { pthread_create(ticketAgent_tid+i, NULL, ticketAgent,NULL); } for (int i = 0; i < 2; ++i) { pthread_join(ticketAgent_tid[i],NULL); } printf("The left ticket is %d\n", ticketAmount); return 0; }
Code Description:
a. pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; // Create a mutex
b. pthread_ mutex_ lock(&lock); // Lock
c. pthread_ mutex_ unlock(&lock); // Unlock
Through the mutex access of critical area based on mutex lock, the problem of two threads accessing critical area at the same time is effectively solved, and the results can be output correctly.
Semaphore based producer consumer synchronization
- Single buffer
The producer and consumer share a buffer, and use the method learned before to create two threads as the producer and consumer respectively. Create a new source code file 2_2.c, as follows:
#include <stdio.h> #include <pthread.h> #include <semaphore.h> // Semaphore header file //Define synchronization semaphore variables (global variables) sem_t empty; sem_t full; //Producer thread function void* producerThd(void* arg){ for(int i=0; i<10; i++){ printf("**Producing one item.**\n"); sem_wait(&empty); //P(empty) printf("**PUTTING item %d to warehouse.**\n",i+1); sem_post(&full); //V(full) } pthread_exit(0); } void* consumerThd(void* arg){ for(int i=0; i<10; i++){ sem_wait(&full); //P(full) printf("##GETTING item %d from warehouse.##\n",i+1); sem_post(&empty); //V(empty) printf("##Consuming the item.##\n"); } pthread_exit(0); } int main(int argc, char *argv[]) { pthread_t producer_tid, consumer_tid; //Semaphore initialization sem_init(&empty, 0, 1); sem_init(&full, 0, 0); pthread_create(&producer_tid, NULL, producerThd, NULL); pthread_create(&consumer_tid, NULL, consumerThd, NULL); pthread_join(producer_tid, NULL); pthread_join(consumer_tid, NULL); //Destroy semaphore sem_destroy(&empty); sem_destroy(&full); }
Use the following command to compile, generate an executable file, and run the program.
gcc 2_2.c -o 2_2 -pthread
./2_2
- Code Description:
To use semaphores, first include the header file < semaphore. H >
sem_t: Data type of semaphore
int sem_init(sem_t *sem, int pshared, unsigned int val);
The first parameter of the function is the semaphore pointer, the second parameter is the semaphore type (generally set to 0), and the third is the initial value of the semaphore. When the second parameter pshared is 0, all threads in the process are available. When it is not 0, different processes are available.
int sem_wait(sem_t *sem);
This function applies for a semaphore. If there is no available semaphore, wait. If there is an available semaphore, occupy a semaphore and subtract 1 from the value of the semaphore.
int sem_post(sem_t *sem);
This function releases a semaphore whose value is increased by 1.
int sem_destory(sem_t *sem);
This function destroys semaphores.
int sem_wait(sem_t *sem);
This function applies for a semaphore. If there is no available semaphore, wait. If there is an available semaphore, occupy a semaphore and subtract 1 from the value of the semaphore. Equivalent to P operation.
int sem_post(sem_t *sem);
This function releases a semaphore whose value is increased by 1. Equivalent to V operation.
Since the producer and consumer share a buffer, the two threads access and execute alternately.
- Multi buffer
When producers and consumers share multiple buffers, set Bank[10] as the warehouse, and there are 10 places to place goods. When the element is 0, it means empty, and when it is 1, it means there are goods. In addition to semaphore synchronization, it is also necessary to add mutually exclusive operation protection critical zone. Create a new source code file 2_3.c, code as follows:
#include <stdio.h> #include <pthread.h> #include <semaphore.h> #include <unistd.h> void printBank(); //This function outputs all the values in the buffer int Bank[10]={0}; int in=0,out=0; sem_t empty; sem_t full; pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; void* producerThd(void* arg){ for(int i=0; i<30; i++){ sem_wait(&empty); pthread_mutex_lock(&lock); //Critical zone start Bank[in] = 1; in = (in+1)%10; printBank(); pthread_mutex_unlock(&lock);//End of critical zone sem_post(&full); } pthread_exit(0); } void* consumerThd(void* arg){ for(int i=0; i<30; i++){ sem_wait(&full); pthread_mutex_lock(&lock); //Critical zone start Bank[out] = 0; out = (out+1)%10; printBank(); pthread_mutex_unlock(&lock);//End of critical zone sem_post(&empty); } pthread_exit(0); } /*This function outputs all the values in the buffer*/ void printBank(){ printf("Bank:"); for(int i=0; i<10; i++){ printf("[%d]",Bank[i]); if(i==9) putchar('\n'); } } int main(int argc, char *argv[]) { pthread_t producer_tid, consumer_tid; sem_init(&empty, 0, 10); sem_init(&full, 0, 0); pthread_create(&producer_tid, NULL, producerThd, NULL); pthread_create(&consumer_tid, NULL, consumerThd, NULL); pthread_join(producer_tid, NULL); pthread_join(consumer_tid, NULL); sem_destroy(&empty); sem_destroy(&full); }
Compile and run the program. You can try to add sleep() function calls to the producer and consumer thread functions, adjust the execution speed and observe the execution results.
Calling sleep(1) in the thread function shows that the output value is different from the result of the sleep() function.
Production and consumption run in a time slice, the for loop output is completed, and the two threads execute alternately.
In the case of single core, the sleep (second) function thread tells the operating system that it does not need scheduling and does not allocate time slices to itself within the second seconds, so that the process is suspended and other processes get cpu resources.
Call the sleep() function so that when one process runs out of time slices, another process starts to execute, which will be different from the execution result without calling the sleep() function.
Solving apple orange problem based on semaphore
- Problem Description:
A plate that can hold N (where N is set to 3) fruits. Father only puts apples on the plate, mother only puts oranges, daughter only eats apples on the plate, and son only eats oranges. Three semaphores are used:
sem_t empty: the semaphore empty controls the number of fruits that can be placed on the plate, which is initially 3, because the plate is empty at the beginning, and the number of fruits that can be placed is 3.
sem_t apple ; The semaphore Apple controls the number of apples that the daughter can eat. The initial value is 0 because there are no apples on the plate at first.
sem_t orange; The semaphore orange controls the number of oranges the son can eat. The initial value is 0, because there are no oranges on the plate at first.
Mutex work_mutex can be consistent when it is printf output.
- Create a new source code file 2_4.c, the reference source code is as follows:
#include <stdio.h> #include <pthread.h> #include <semaphore.h> #include <unistd.h> sem_t empty; //Control the amount of fruit on the plate sem_t apple; //Control the number of apples sem_t orange; //Control the number of oranges pthread_mutex_t work_mutex=PTHREAD_MUTEX_INITIALIZER; //Declare mutex work_mutex int fruitCount = 0;//The amount of fruit on the plate void *thread_f(void *arg) //father thread { while(1) { sem_wait(&empty); //Take up a plate space, and the number of fruits can be reduced by 1 pthread_mutex_lock(&work_mutex); //Lock printf("Dad put in an apple!(Total current fruit on plate:%d)\n", ++fruitCount); sem_post(&apple); //An apple signal is released. The number of apples you can eat is increased by 1 pthread_mutex_unlock(&work_mutex); //Unlock sleep(0.1); } } void * thread_m(void *arg) //mother thread { while(1){ sem_wait(&empty); pthread_mutex_lock(&work_mutex); //Lock printf("Mom put in an orange!(Total current fruit on plate:%d)\n", ++fruitCount); sem_post(&orange); pthread_mutex_unlock(&work_mutex); //Unlock sleep(0.2); } } void * thread_s(void *arg) //son thread { while(1){ sem_wait(&orange); //If you occupy an orange semaphore, the number of oranges you can eat will be reduced by 1 pthread_mutex_lock(&work_mutex); //Lock printf("The son ate an orange!(Total current fruit on plate:%d)\n", --fruitCount); sem_post(&empty); //Eat an orange and free up space on a plate. Add 1 to the number of fruits pthread_mutex_unlock(&work_mutex); //Unlock sleep(0.2); } } void * thread_d(void *arg) //Daught thread { while(1){ sem_wait(&apple); pthread_mutex_lock(&work_mutex); //Lock printf("The daughter ate an apple!(Total current fruit on plate:%d)\n", --fruitCount); sem_post(&empty); pthread_mutex_unlock(&work_mutex); //Unlock sleep(0.1); } } int main() { pthread_t father; //Define thread pthread_t mother; pthread_t son; pthread_t daughter; sem_init(&empty, 0, 3); //Semaphore initialization sem_init(&apple, 0, 0); sem_init(&orange, 0, 0); pthread_create(&father,NULL, thread_f,NULL); //Create thread pthread_create(&mother,NULL, thread_m,NULL); pthread_create(&daughter,NULL, thread_d,NULL); pthread_create(&son,NULL, thread_s,NULL); sleep(1); sem_destroy(&empty); sem_destroy(&apple); sem_destroy(&orange); return 0; }
Compile and run the program many times, give the execution results and think about the execution.
Use mutually exclusive semaphore work_ Mutex, pthread_ mutex_ lock(&work_mutex); // Lock, pthread_ mutex_ unlock(&work_mutex); // Unlocking realizes the mutually exclusive access of the four processes to the critical resource fruitCount.
Semaphore based critical zone management
Revision 2_1.c, try to use semaphores to solve the synchronization problem of ticket booking threads, and give the source code and implementation results.
#include <stdio.h> #include <pthread.h> #include <unistd.h> #include <semaphore.h> int ticketAmount = 2; //Global Variable sem_t mutex; //Mutually exclusive semaphore void* ticketAgent(void* arg){ sem_wait(&mutex); //P(mutex) int t = ticketAmount; if (t > 0) { printf("One ticket sold!\n"); t--; }else{ printf("Ticket sold out!!\n"); } ticketAmount = t; sem_post(&mutex); //V(mutex) pthread_exit(0); } int main(int argc, char const *argv[]) { pthread_t ticketAgent_tid[2]; sem_init(&mutex,0,1); //The initialization semaphore is 1 //Create two threads through a loop based on the same thread function for (int i = 0; i < 2; ++i) { pthread_create(ticketAgent_tid+i, NULL, ticketAgent,NULL); } //The loop waits for all threads to end for (int i = 0; i < 2; ++i) { pthread_join(ticketAgent_tid[i],NULL); } printf("The left ticket is %d\n", ticketAmount); sem_destroy(&mutex); //Undo semaphore return 0; }
The operation results are as follows:
Solving driver conductor problem based on semaphore
Create a new source code file 2_5.c. realize the synchronization process of driver and conductor in classroom PPT through semaphore mechanism.
#include<stdio.h> #include<pthread.h> #include<semaphore.h> sem_t semid1,semid2;//Semid1 vehicle; Semid2 gate void* driver() { int n=6; while(n) { n--; printf("\n"); sem_wait(&semid1); printf("The driver started the car\n"); printf("The driver drives normally\n"); printf("The driver stops at the station\n"); sem_post(&semid2); } } void* passenger() { int n=6; while(n) { n--; printf("The conductor closed the door\n"); sem_post(&semid1); printf("The conductor sells tickets\n"); sem_wait (&semid2); printf("The conductor opened the door\n"); } } int main() { sem_init(&semid1,0,0); sem_init(&semid2,0,0); pthread_t tid1,tid2; pthread_create(&tid1,0,driver,0); pthread_create(&tid2,0,passenger,0); pthread_join(tid1,(void**)0); pthread_join(tid2,(void**)0); return 0; }
The operation results are as follows:
Solve the driver conductor problem based on semaphores semid1 and semid2 (semid1 represents vehicle and semid2 represents door), and carry out SEM for the corresponding semaphores_ post(),sem_wait() implements program functions.