C语言:详细说明线程的同步操作:互斥,无名信号量,条件变量,以及需要注意的一些问题

一.线程的互斥

1.1概念:

在多线程中,如果多线程同时在访问同一个全局变量,就会出现多个线程在获取变量值时候获取的是同一个值,此时在线程中操作这个变量就会出现不同步的效果。下面这幅图就能够演示出对应的效果。可以使用线程的互斥锁来解决这样的问题。

1.2代码实现:

#include 
#include 
#include 
#include 

#define ERROR(msg) do{\
    printf("%s %s %d\n", __FILE__, __func__, __LINE__);\
    printf(msg);\
    exit(-1); \
}while(0)

pthread_mutex_t lock;
int money = 1000;

void* task1(void* argc)
{
    while (1){
        sleep(1);
        pthread_mutex_lock(&lock);
        
        if (money > 0){
            money -= 50;
            printf("张三取了50块钱,还剩下%d\n",money);
        }else {
            printf("张三取钱失败\n");
        }

        pthread_mutex_unlock(&lock);
    }
}
 void* task2(void* argc)
 {
    while (1){
        sleep(1);
        pthread_mutex_lock(&lock);
        
        if (money > 0){
            money -= 100;
            printf("李四取了100块钱,还剩下%d\n",money);
        }else {
            printf("李四取钱失败\n");
        }

        pthread_mutex_unlock(&lock);
    }
 }


int main(int argc, char const *argv[])
{
    pthread_t pid1, pid2;
    pthread_mutex_init(&lock, NULL);

    if (pthread_create(&pid1, NULL, task1, NULL)){
        ERROR("创建失败");
    }

    if (pthread_create(&pid2, NULL, task2, NULL)){
        ERROR("create error");
    }

    pthread_join(pid1, NULL);
    pthread_join(pid2, NULL);

    pthread_mutex_destroy(&lock);
    return 0;
}

二.无名信号量:

2.1概念:

线程同步,提前已经知道线程的执行顺序,让线程顺序执行的过程就是同步。

典型的就是生产者和消费者模型。

2.2代码实现:

#include 
#include 
#include 
#include 

#define ERROR(msg) do{\
    printf("%s %s %d\n", __FILE__, __func__, __LINE__);\
    printf(msg);\
    exit(-1); \
}while(0)

sem_t sem1, sem2;

void* task1(void* argc)
{
    while(1){
        sem_wait(&sem1);
        printf("我生产了一辆汽车\n");
        sem_post(&sem2);
    }
}

void* task2(void* argc)
{
    while(1){
        sem_wait(&sem2);
        printf("我买了一辆汽车\n");
        sem_post(&sem1);
    }
}


int main(int argc, char const *argv[])
{
    pthread_t pid1, pid2;
    sem_init(&sem1, 0, 1);
    sem_init(&sem2, 0, 0);

    if (pthread_create(&pid1, NULL, task1, NULL)){
        ERROR("create error");
    }

    if (pthread_create(&pid2, NULL, task2, NULL)){
        ERROR("create error");
    }

    pthread_join(pid1, NULL);
    pthread_join(pid2, NULL);

    sem_destroy(&sem1);
    sem_destroy(&sem2);
    return 0;
}

三.条件变量

3.1条件变量相比于无名信号量的区别:

无名信号量适合在线程数比较少的线程中实现同步过程,而条件变量适合在大量线程

实现同步过程。例如条件变量的使用场景如下:比如你要编写一个12306买票的服务器

当客户端访问服务器的时候,服务器会创建一个线程服务于这个用户。如果有多个用户

同时想买票,此时服务需要在瞬间创建一堆线程,这个时间比较长,对用户的体验感不好。

所以12306服务是在启动的时候都已经创建好一堆线程。调用pthread_cond_wait让这些

线程休眠,当有客户端请求买票的时候,只需要唤醒这些休眠的线程即可,有于省去了

创建线程的时候,所以这种方式的效率非常的高。

3.2代码实现:

#include 
#include 
#include 
#define ERROR(msg) do{\
    printf("%s %s %d\n", __FILE__, __func__, __LINE__);\
    printf(msg);\
    exit(-1); \
}while(0)

pthread_cond_t cond;
pthread_mutex_t lock;
int i = 0;

void* task1(void* argc)
{
    while (1){
        pthread_mutex_lock(&lock);
        while(i != 0){
            pthread_cond_wait(&cond,&lock);
        }
        printf("我生产了一辆汽车\n");
        i = 1;
        pthread_cond_signal(&cond);
        pthread_mutex_unlock(&lock);
    }
}

void* task2(void* argc)
{
    while (1){
        pthread_mutex_lock(&lock);
        while(i == 0){
            pthread_cond_wait(&cond,&lock);
        }
        printf("我买了一辆汽车\n");
        i = 0;
        pthread_cond_signal(&cond);
        pthread_mutex_unlock(&lock);
    }
}

int main(int argc, char const *argv[])
{
    pthread_t pid1, pid2;
    pthread_mutex_init(&lock, NULL);
    pthread_cond_init(&cond, NULL);
    
    if (pthread_create(&pid1, NULL, task1, NULL)){
        ERROR("create error");
    }

    if(pthread_create(&pid2, NULL, task2, NULL)){
        ERROR("create error");
    }
    
    pthread_join(pid1, NULL);
    pthread_join(pid2, NULL);

    pthread_cond_destroy(&cond);
    pthread_mutex_destroy(&lock);
    return 0;
}

3.3讲解一下为什么这个地方要用while而不用if

如果用if的话,假如是一号线程先抢到这个锁,然后生产了一辆汽车,然后唤醒了一个线程,因为此时还没有线程,然后释放锁,这个时候如果是二号进程抢到锁,那用if和while都一样,但是如果还是一号线程抢到这个锁,然后进入if i!= 0,然后由于上次唤醒的一个线程还没有用,所以此时立马唤醒,然后又生产了一辆车,这个时候就出现问题了,但是如果用while就不会,因为此时,必须得二号进程,把i == 0,这个才能往下走,不然一号进程一直在那个while循环里面。

你可能感兴趣的:(c语言,c)