Thread synchronization, mutex, deadlock, read-write lock

A case of using multithreading to realize ticket purchase. There are 3 windows, a total of 100 tickets

Version 1: The problem of thread synchronization is not considered, and there will be situations where multiple windows buy the same ticket

*/


#include <stdio.h>
#include <pthread.h>
#include <unistd.h>

// Global variables, all threads share this resource.
int tickets = 100;

void * sellticket(void * arg) {<!-- -->
    // sell tickets
    while(tickets > 0) {<!-- -->
        usleep(6000);
        printf("%ld is selling %d tickets\\
", pthread_self(), tickets);
        tickets--;
    }
    return NULL;
}

int main() {<!-- -->

    // 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);

    // Recycle the resources of the child thread, blocking
    pthread_join(tid1, NULL);
    pthread_join(tid2, NULL);
    pthread_join(tid3, NULL);

    // Set thread detachment.
    //pthread_detach(tid1);
    //pthread_detach(tid2);
    //pthread_detach(tid3);

    pthread_exit(NULL); // exit the main thread

    return 0;
}


Version 2: Add mutex to achieve synchronization, avoiding the above mistakes

 type of mutex pthread_mutex_t
    int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);
        - Initialize the mutex
        - parameters:
            - mutex: the mutex variable that needs to be initialized
            - attr: Mutex-related attributes, NULL
        - restrict : A modifier of the C language, the modified pointer cannot be operated by another pointer.
            pthread_mutex_t *restrict mutex = xxx;
            pthread_mutex_t * mutex1 = mutex;

    int pthread_mutex_destroy(pthread_mutex_t *mutex);
        - release mutex resources

    int pthread_mutex_lock(pthread_mutex_t *mutex);
        - Locking, blocking, if one thread is locked, then other threads can only block and wait

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

    int pthread_mutex_unlock(pthread_mutex_t *mutex);
        - unlock
*/


#include <stdio.h>
#include <pthread.h>
#include <unistd.h>

// Global variables, all threads share this resource.
int tickets = 1000;

// create a mutex
pthread_mutex_t mutex;

void * sellticket(void * arg) {<!-- -->

    // sell tickets
    while(1) {<!-- -->

        // lock
        pthread_mutex_lock( & mutex);

        if(tickets > 0) {<!-- -->
            usleep(6000);
            printf("%ld is selling %d tickets\\
", pthread_self(), tickets);
            tickets--;
        } else {<!-- -->
            // unlock
            pthread_mutex_unlock( & mutex);
            break;
        }

        // unlock
        pthread_mutex_unlock( & mutex);
    }

    

    return NULL;
}

int main() {<!-- -->

    // Initialize the 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);

    // Recycle the resources of the child thread, blocking
    pthread_join(tid1, NULL);
    pthread_join(tid2, NULL);
    pthread_join(tid3, NULL);

    pthread_exit(NULL); // exit the main thread

    // Release the mutex resource
    pthread_mutex_destroy( & mutex);

    return 0;
}

The first two are relatively simple, no demonstration, mainly demonstrate the third one

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>

// Create 2 mutexes
pthread_mutex_t mutex1, mutex2;

void * workA(void * arg) {<!-- -->

    pthread_mutex_lock( & mutex1);
    sleep(1);
    pthread_mutex_lock( & mutex2);

    printf("workA....\\
");

    pthread_mutex_unlock( & mutex2);
    pthread_mutex_unlock( & mutex1);
    return NULL;
}


void * workB(void * arg) {<!-- -->
    pthread_mutex_lock( & mutex2);
    sleep(1);
    pthread_mutex_lock( & mutex1);

    printf("workB....\\
");

    pthread_mutex_unlock( & mutex1);
    pthread_mutex_unlock( & mutex2);

    return NULL;
}

int main() {<!-- -->

    // Initialize the mutex
    pthread_mutex_init( & mutex1, NULL);
    pthread_mutex_init( & mutex2, NULL);

    // Create 2 child threads
    pthread_t tid1, tid2;
    pthread_create( & tid1, NULL, workA, NULL);
    pthread_create( & tid2, NULL, workB, NULL);

    // Recycle child thread resources
    pthread_join(tid1, NULL);
    pthread_join(tid2, NULL);

    // Release the mutex resource
    pthread_mutex_destroy( & mutex1);
    pthread_mutex_destroy( & mutex2);

    return 0;
}

 The type of read-write lock pthread_rwlock_t
    int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock,
    const pthread_rwlockattr_t *restrict attr);
    int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
    int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
    int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);
    int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
    int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);
    int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);

    Case: 8 threads operate the same global variable.
    3 threads write this global variable from time to time, and 5 threads read this global variable from time to time
*/


#include <stdio.h>
#include <pthread.h>
#include <unistd.h>

// Create a shared data
int num = 1;
// pthread_mutex_t mutex;
pthread_rwlock_t rwlock;

void * writeNum(void * arg) {<!-- -->

    while(1) {<!-- -->
        pthread_rwlock_wrlock( & amp; rwlock);
        num++;
        printf(" + + write, tid : %ld, num : %d\\
", pthread_self(), num);
        pthread_rwlock_unlock( & rwlock);
        usleep(100);
    }

    return NULL;
}

void * readNum(void * arg) {<!-- -->

    while(1) {<!-- -->
        pthread_rwlock_rdlock( & rwlock);
        printf("===read, tid : %ld, num : %d\\
", pthread_self(), num);
        pthread_rwlock_unlock( & rwlock);
        usleep(100);
    }

    return NULL;
}

int main() {<!-- -->

   pthread_rwlock_init( & rwlock, NULL);

    // Create 3 write threads and 5 read threads
    pthread_t wtids[3], rtids[5];
    for(int i = 0; i < 3; i ++ ) {<!-- -->
        pthread_create( &wtids[i], NULL, writeNum, NULL);
    }

    for(int i = 0; i < 5; i ++ ) {<!-- -->
        pthread_create( &rtids[i], NULL, readNum, NULL);
    }

    // set thread separation
    for(int i = 0; i < 3; i ++ ) {<!-- -->
       pthread_detach(wtids[i]);
    }

    for(int i = 0; i < 5; i ++ ) {<!-- -->
         pthread_detach(rtids[i]);
    }

    pthread_exit(NULL);

    pthread_rwlock_destroy( & rwlock);

    return 0;
}