2022-2-1 Niuke C + + project - thread synchronization

First Edition: different threads sell each other, which will lead to the final purchase of 300 tickets
Because 100 is placed in local variables, each thread has one

/*
Case of using multithreading to buy tickets
 There are three windows, a total of 100 tickets
*/
#include<stdio.h>
#include<pthread.h>
#include<stdlib.h>
void *sellticket(void*arg){
    int ticket = 100;
    while (ticket > 0)
    {
        printf("%ld is selling the %d ticket.\n",pthread_self(),ticket);
        ticket--;
    }
    return NULL;
}
int main(void){
    //Create 3 child threads
    pthread_t tid1,tid2,tid3;
    pthread_create(&tid1,NULL,sellticket,NULL);
    pthread_create(&tid2,NULL,sellticket,NULL);
    pthread_create(&tid3,NULL,sellticket,NULL);

    //Reclaim resources from child threads
    pthread_join(tid1,NULL);
    pthread_join(tid2,NULL);
    pthread_join(tid3,NULL);

    //Set thread separation
    // pthread_detach(tid1);
    // pthread_detach(tid2);
    // pthread_detach(tid3);

    //Exit main thread
    pthread_exit(NULL);
    
    return 0;
}


Version 2: even if the global variable is set, the previous thread will change but the subsequent thread will not get the changed data.


Third Edition: let each thread sleep for 3000 ms, and the effect of repeatedly changing a certain data before the result is more obvious


Fourth Edition: change to sleep 6000 ms, there is a surprise, but there is a negative number!!!

/*
Case of using multithreading to buy tickets
 There are three windows, a total of 100 tickets
*/
#include<stdio.h>
#include<pthread.h>
#include<stdlib.h>
#include<unistd.h>
int ticket = 100;
void *sellticket(void*arg){
    
    while (ticket > 0)
    {
        usleep(6000);
        printf("%ld is selling the %d ticket.\n",pthread_self(),ticket);
        ticket--;
    }
    return NULL;
}
int main(void){
    //Create 3 child threads
    pthread_t tid1,tid2,tid3;
    pthread_create(&tid1,NULL,sellticket,NULL);
    pthread_create(&tid2,NULL,sellticket,NULL);
    pthread_create(&tid3,NULL,sellticket,NULL);

    //Reclaim resources from child threads
    pthread_join(tid1,NULL);
    pthread_join(tid2,NULL);
    pthread_join(tid3,NULL);

    //Set thread separation
    // pthread_detach(tid1);
    // pthread_detach(tid2);
    // pthread_detach(tid3);

    //Exit main thread
    pthread_exit(NULL);
    
    return 0;
}


Why negative numbers?
When the last ticket is left, (i = = 1)
Thread 1, thread 2 and thread 3 seize the CPU together,
When thread 1 comes in, it meets the conditions (I = = 1 > 0), enters the loop, and thread 1 is forced to sleep;
Give it to thread 2. Thread 2 meets the conditions (I = = 1 > 0), enters the loop, and thread 2 is forced to sleep;
Give it to thread 3. Thread 3 meets the conditions (I = = 1 > 0), enters the loop, and thread 3 is forced to sleep;
When thread 1 wakes up, it then loops, – i, and finally i = = 0;
When thread 2 wakes up, it then loops, – i, and finally i = = -1;
When thread 3 wakes up, it then loops, – i, and finally i == -2;
Because there is no judgment when you wake up and execute it again

How to solve it?
1) When operating on variable i, other threads cannot operate on i;
2) When a thread performs a series of operations, those operations must be atomic operations. Not half of the other threads entered the loop

Plus mutex


Lock before entering the critical zone, but there will be a problem:
The final execution result is executed by the same thread.
Why does this happen?
The logic of locking in that way is: locking enters the cycle and will not be unlocked until the cycle is completed, and the tickets at the end of the cycle have been sold out.

Finally, it is changed to: the loop can be entered freely, and the data volume should be locked when it is modified.

/*
    Type of mutex pthread_mutex_t
    int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);
            - Initialize mutex
            - mutex Mutex variables that need to be initialized
            - attr Mutex related attributes. If NULL is passed, the default attribute is used
        - restrict : C The modifier of language. The modified pointer cannot be operated by another pointer

            pthread_mutex_t *restrict mutex = xxx;
            pthread_mutex_t * mutex1 = mutex;
            ((mutex1 cannot operate on mutex due to the restrict ion)
            (This operator is to limit the operation permission of the pointer enjoyed by an object.)

    int pthread_mutex_destroy(pthread_mutex_t *mutex);
        - Release mutually exclusive resources

    int pthread_mutex_lock(pthread_mutex_t *mutex);
        - Locking, blocking. If one thread locks, other threads can only block and wait

    int pthread_mutex_trylock(pthread_mutex_t *mutex);
        - Try locking. If locking fails, it will not block and will return directly

    int pthread_mutex_unlock(pthread_mutex_t *mutex);
        - Unlock
*/
#include<stdio.h>
#include<pthread.h>
#include<stdlib.h>
#include<unistd.h>
int ticket = 1000;

//Create a mutex
pthread_mutex_t mutex;
// //Writing method of the first edition: in the end, there is only one thread selling tickets

// void *sellticket(void*arg){
//     //Lock
//     pthread_mutex_lock(&mutex);
//     //Business logic
//     while (ticket > 0)
//     {
//         usleep(6000);
//         printf("%ld is selling the %d ticket.\n",pthread_self(),ticket);
//         ticket--;
//     }
//     //Unlock
//     pthread_mutex_unlock(&mutex);
//     return NULL;
// }

//Second edition writing method
void *sellticket(void*arg){
// //Lock
//     pthread_mutex_lock(&mutex);
//Business logic
    while (1)
    {
        //Lock
        pthread_mutex_lock(&mutex);
        
        if(ticket > 0){
            usleep(6000);
            printf("%ld is selling the %d ticket.\n",pthread_self(),ticket);
            ticket--;
        }else{
         //It also needs to be unlocked here. Otherwise, if you jump out of the loop, the lock will not be released, causing the thread to fail to end
            //Unlock
            pthread_mutex_unlock(&mutex);
            break;
        }
        //Unlock
        pthread_mutex_unlock(&mutex);
    }
    
    return NULL;
    
}

int main(void){
    //Initialize mutex
    pthread_mutex_init(&mutex,NULL);
    //Create 3 child threads
    pthread_t tid1,tid2,tid3;
    pthread_create(&tid1,NULL,sellticket,NULL);
    pthread_create(&tid2,NULL,sellticket,NULL);
    pthread_create(&tid3,NULL,sellticket,NULL);

    //Reclaim resources from child threads
    pthread_join(tid1,NULL);
    pthread_join(tid2,NULL);
    pthread_join(tid3,NULL);

    //Exit main thread
    pthread_exit(NULL);
    //Release mutex resources
    pthread_mutex_destroy(&mutex);
    
    return 0;
}

The final running result: you can see the thread switching

Keywords: C++ Back-end

Added by ek5932 on Thu, 03 Feb 2022 10:08:37 +0200