[Mutex and semaphore] Thread communication: mutex and semaphore

Table of contents

0. Mutex locks and semaphores

Synchronous Mutex Overview

1. Mutex lock

1.1 The concept of mutex lock

1.2 Mutex lock initialization: pthread_mutex_init function

1.3 Mutex lock: pthread_mutex_lock function

1.4 Mutex unlocking: pthread_mutex_unlock function

1.5 Destroy the mutex lock: pthread_mutex_destroy function

1.6 Mutex lock case

1.61 Consequences of not using mutex locks

 1.62 Results of using mutex locks 

2. Signal amount

2.1 The concept of semaphore

2.2 Initialization of semaphore: sem_init function

2.3 P operation of semaphore: sem_wait function

2.4 V operation of semaphore: sem_post function

2.5 Get the count value of the semaphore: sem_getvalue function

2.6 Destruction of semaphore: sem_destroy function

2.7 Use of semaphores

2.7.1 Semaphore implements mutual exclusion function

2.7.2 Semaphore realizes synchronization function

 Summarize:


0. Mutex locks and semaphores

Synchronous Mutex Overview

        In a multitasking operating system, multiple tasks running simultaneously may need to access/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.

Mutually exclusive:

        A public resource can only be used by one process or thread at a time, and multiple processes or threads cannot use public resources at the same time. There are two main methods for process and thread synchronization and mutual exclusion in the POSIX standard: semaphores and mutex locks.

Synchronize:

        Two or more processes or threads synchronize during operation and run in a predetermined order.

        Synchronization is order based on mutual exclusion.

1. Mutex lock

1.1 The concept of mutex lock

        Mutex is a simple locking method to control access to shared resources. Mutex has only two states, namely lock and unlock. Before accessing this resource, you should first apply for a mutex.

  •         If the mutex is in the unlocked state, the mutex will be applied for and locked immediately.
  •         If the mutex is in the lock state, the applicant is blocked by default. The unlock operation should be performed by the locker.

Initialize the mutex lock: pthread_mutex_init function.

        Mutex is represented by the pthread_mutex_t data type. Before using the mutex lock, it must be initialized.

Static allocated mutex lock:

        pthread_mutex_t mutex =   PTHREAD_MUTEX_INITIALIZER;

Dynamically allocate mutex lock:

        pthread_mutex_t mutex;

Destroy the mutex lock: pthread_mutex_destroy

1.2 Mutex lock initialization: pthread_mutex_init function

pthread_mutex_init function:

#include<pthread.h>

int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr);

Function:

        Initialize a mutex lock.

parameter:

        mutext: the address of the mutex lock.

        attr: attribute of the mutex lock, NULL is the default attribute.

return value:

        Success: 0

        Failure: non-0

1.3 Mutex lock: pthread_mutex_lock function

pthread_mutex_lock function

#include<pthread.h>

int pthread_mutex_lock(pthread_mutex_t *mutex);

Function:

        Lock the mutex. If it is already locked, the caller blocks until the mutex is unlocked.

parameter:

        mutex: the specified mutex lock.

return value:

        Success: 0

        Failure: non-0

#include<pthread.h>

int pthread_mutex_trylock(pthread_mutex_t * mutex);

Function:

        Lock the mutex. If it is already locked, the lock fails and the function returns immediately.

parameter:

        mutex: mutex lock address.

return value:

        Success: 0

        Failure: non-0 

1.4 Mutex unlocking: pthread_mutex_unlock function

pthread_mutex_unlock function

#include<pthread.h>

int pthread_mutex_unlock(pthread_mutex_t *mutex);

Function:

        Unlocks the specified mutex.

parameter:

        mutex: mutex lock address.

return value:

        Success: 0

        Failure: non-0

1.5 Destroy the mutex lock: pthread_mutex_destroy function

pthread_mutex_destroy function

#include<pthread.h>

int pthread_mutex_destroy(pthread_mutex_t *mutex);

Function:

        Destroys the specified mutex lock.

parameter:

        mutex: mutex lock address.

return value:

        Success: 0

        Failure: non-0

1.6 Mutex lock case

1.61 Consequences of not using mutex locks
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<pthread.h>

int money = 10000;

void* pthread_fun1(void* arg)
{
    int get, yu, shiji;
    get = 10000;

    printf("zhangsan look balance\n");
    sleep(1);
    yu = money;

    printf("zhangsan spent money\n");
    sleep(1);
    if (get > yu)
    {
        shiji = 0;

    }
    else
    {
        shiji = get;
        yu = yu - get;
        money = yu;
    }
    printf("zhangsan want spent %d, fact spent is %d, balance is %d\n", get, shiji, yu);
    pthread_exit(NULL);
}
void* pthread_fun2(void* arg)
{
    int get, yu, shiji;
    get = 10000;

    printf("lisi look balance\n");
    sleep(1);
    yu = money;

    printf("lisi spent money\n");
    sleep(1);
    if (get > yu)
    {
        shiji = 0;

    }
    else
    {
        shiji = get;
        yu = yu - get;
        money = yu;
    }
    printf("lisi want spent %d, fact spent is %d, balance is %d\n", get, shiji, yu);
    pthread_exit(NULL);
}
int main()
{

    pthread_t thread1, thread2;

    if (pthread_create(&thread1, NULL, pthread_fun1, NULL) != 0)
    {
        perror("fail to pthread_create");
        exit(1);
    }
    if (pthread_create(&thread2, NULL, pthread_fun2, NULL) != 0)
    {
        perror("fail to pthread_creat");
        exit(1);
    }

    pthread_join(thread1, NULL);
    pthread_join(thread2, NULL);
    return 0;
}

Execution screenshot:

 1.62 Results of using mutex locks 

Code example:

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

int money = 10000;
//第一步:创建互斥锁
pthread_mutex_t mutex;

void* pthread_fun1(void* arg)
{
    int get, yu, shiji;
    get = 10000;
    //第三步,互斥锁上锁
    pthread_mutex_lock(&mutex);
    printf("zhangsan look balance\n");
    sleep(1);
    yu = money;

    printf("zhangsan spent money\n");
    sleep(1);
    if (get > yu)
    {
        shiji = 0;

    }
    else
    {
        shiji = get;
        yu = yu - get;
        money = yu;
    }
    printf("zhangsan want spent %d, fact spent is %d, balance is %d\n", get, shiji, yu);
    //第四步,互斥锁解锁
    pthread_mutex_unlock(&mutex);
    pthread_exit(NULL);
}
void* pthread_fun2(void* arg)
{
    int get, yu, shiji;
    get = 10000;
    //第三步,互斥锁上锁
    pthread_mutex_lock(&mutex);
    printf("lisi look balance\n");
    sleep(1);
    yu = money;

    printf("lisi spent money\n");
    sleep(1);
    if (get > yu)
    {
        shiji = 0;

    }
    else
    {
        shiji = get;
        yu = yu - get;
        money = yu;
    }
    printf("lisi want spent %d, fact spent is %d, balance is %d\n", get, shiji, yu);
    //第四步,互斥锁解锁
    pthread_mutex_unlock(&mutex);
    pthread_exit(NULL);
}
int main()
{
    //第二步,初始化互斥锁
    pthread_mutex_init(&mutex, NULL);
    pthread_t thread1, thread2;

    if (pthread_create(&thread1, NULL, pthread_fun1, NULL) != 0)
    {
        perror("fail to pthread_create");
        exit(1);
    }
    if (pthread_create(&thread2, NULL, pthread_fun2, NULL) != 0)
    {
        perror("fail to pthread_creat");
        exit(1);
    }

    pthread_join(thread1, NULL);
    pthread_join(thread2, NULL);
    //第五步,销毁互斥锁
    pthread_mutex_destroy(&mutex);
    return 0;
}

Results of the:

2. Signal amount

2.1 The concept of semaphore

        Semaphores are widely used for synchronization and mutual exclusion between processes or threads. A semaphore is essentially a non-negative integer counter, which is used to control access to public resources.

        When programming, you can determine whether you have access permission to the public resource based on the result of operating the semaphore value. When the semaphore value is greater than 0, you can access it, otherwise it will be blocked.

        The semaphore is also called the PV operation. PV is the operation on the semaphore. A P operation reduces the semaphore sem by 1, and a V operation makes the semaphore sem+1. For the P operation, if the sem value of the semaphore is less than or equal to 0, the P operation will block. If the value of the semaphore is greater than 0, the P operation can be performed to subtract 1.

Semaphores are mainly used in two typical situations: synchronization and mutual exclusion between processes or threads.

  • If used for mutual exclusion, several processes (or threads) often only set one semaphore.
  • If used for synchronization operations, multiple semaphores are often set up and different initial values ​​are arranged to achieve the execution sequence between them.

Semaphores are used for mutual exclusion:

Semaphores are used for synchronization:

 

2.2 Initialization of semaphore: sem_init function

sem_init function

#include<semaphore.h>

int sem_init(sem_t *sem, int pshared, unsigned int value);

Function:

        Create a semaphore and initialize its value.

parameter:

        sem: The address of the semaphore.

        pshared: equal to 0, the semaphore is shared between threads; not equal to 0, the semaphore is shared between processes.

        value: The initial value of the semaphore.

return value:

        Success: 0

        Failure: -1

2.3 P operation of semaphore: sem_wait function

sem_wait function

#include<semaphore.h>

int sem_wait(sem_t *sem);

Function:

        Decrease the value of the semaphore by 1. If the value of the semaphore is less than or equal to 0, this function will cause the caller to block.

parameter:

        sem: The address of the semaphore.

return value:

        Success: 0

        Failure: -1

#include<semaphore.h>

int sem_trywait(sem_t *sem);

Function:

        Decrement the value of the semaphore by 1. If the value of the semaphore is less than 0, the operation on the semaphore fails and the function returns immediately.

parameter:

        sem: semaphore address.

return value:

        Success: 0

        Failure: -1

2.4 V operation of semaphore: sem_post function

sem_post function

#include<semaphore.h>

int sem_post(sem_t *sem);

Function:

        Increase the value of the semaphore by 1 and send a signal to wake up the waiting thread.

parameter:

        sem: semaphore address.

return value:

        Success: 0

        Failure: -1 

2.5 Get the count value of the semaphore: sem_getvalue function

sem_getvalue function

#include<semaphore.h>

int sem_getvalue(sem_t *sem, int *sval);

Function:

        Get the value of the semaphore identified by sem and save it in sval.

parameter:

        sem: semaphore address.

        sval: The address where the semaphore value is saved.

return value:

        Success: 0

        Failure: -1

2.6 Destruction of semaphore: sem_destroy function

sem_destroy function

#include<semaphore.h>

int sem_destroy(sem_t *sem);

Function:        

        Delete the semaphore identified by sem.

parameter:

        sem: semaphore address.

return value:

        Success: 0

        Failure: -1

2.7 Use of semaphores

2.7.1 Semaphore implements mutual exclusion function

Code example:

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

//第一步,创建信号量
sem_t sem;

void printer(char* str)
{
    //第三步,P操作
    sem_wait(&sem);
    while (*str)
    {
        putchar(*str);
        fflush(stdout);
        str++;
        sleep(1);
    }
    //第四步,V操作
    sem_post(&sem);
}

void* thread_fun1(void* arg)
{
    char* str1 = "hello";
    printer(str1);
}
void* thread_fun2(void* arg)
{
    char* str2 = "world";
    printer(str2);
}

int main()
{
    //第二步,初始化信号量
    sem_init(&sem, 0, 1);
    pthread_t tid1, tid2;

    pthread_create(&tid1, NULL, thread_fun1, NULL);
    pthread_create(&tid2, NULL, thread_fun2, NULL);

    pthread_join(tid1, NULL);
    pthread_join(tid2, NULL);

    printf("\n");
    //第五步,销毁信号量
    sem_destroy(&sem);
    return 0;
}

Execution screenshot:

2.7.2 Semaphore realizes synchronization function

Code example:

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

char ch = 'a';
//第一步,创建两个信号量
sem_t sem_g, sem_p;

void* pthread_g(void* arg)
{
    while (ch <= 'z')
    {
        //第四步,后执行线程
        sem_wait(&sem_g);
        ch++;
        sleep(1);
        //第六步,V操作
        sem_post(&sem_p);
    }
}

void* pthread_p(void* arg)
{
    while (ch < 'z')
    {
        //第三步,先执行线程
        sem_wait(&sem_p);
        printf("%c", ch);
        fflush(stdout);
        //第五步,V操作
        sem_post(&sem_g);
    }
}
int main()
{
    //第二步,初始化信号量
    sem_init(&sem_g, 0, 0);
    sem_init(&sem_p, 0, 1);
    pthread_t tid1, tid2;

    pthread_create(&tid1, NULL, pthread_g, NULL);
    pthread_create(&tid2, NULL, pthread_p, NULL);

    pthread_join(tid1, NULL);
    pthread_join(tid2, NULL);

    printf("\n");
    //第七步,销毁信号量
    sem_destroy(&sem_g);
    sem_destroy(&sem_p);
    return 0;
}

Execution screenshot:

 Summarize:

        In general, mutexes and semaphores are important tools for thread and process synchronization. Mutex locks are mainly used to protect resources and ensure that only one thread or process accesses a certain resource at the same time, thereby avoiding concurrency problems. Semaphores are more used for communication and synchronization between threads and processes, controlling concurrent access within a certain range, and providing more fine-grained concurrency control for programs.

        Mastering the use and differences of mutexes and semaphores can greatly improve the efficiency and stability of multi-thread and multi-process programming.

Guess you like

Origin blog.csdn.net/crr411422/article/details/131421923