Linux 线程同步——条件变量

一、条件变量的概念

如果说互斥锁是用于同步线程对共享数据的访问的话,那么条件变量则是用于在线程之间同步共享数据的值。条件变量提供了一种线程间的通知机制:当某个共享数据达到某个值的时候,唤醒等待这个共享数据的线程。如下图所示:

二、条件变量相关函数

1.pthread_cond_init()初始化条件变量

参数解释:

第一个参数cond:条件变量

第二个参数attr:条件变量属性,通常传NULL,表示使用默认属性

2.pthread_cond_wait()等待条件变量满足

功能:阻塞等待一个条件变量(等待唤醒)并解除相应的锁资源

参数解释:

第一个参数cond:条件变量

第二个参数mutex:是用于保护条件变量的互斥锁,以确保pthread_cond_wait操作的原子性。在调用pthread_cond_wait之前必须确保互斥锁mutex已经加锁,否则将会出现多个线程同时要进入等待队列的情况导致不可预期的结果。pthread_cond_wait在执行时,首先把调用线程放入条件变量的等待队列中,然后将互斥锁解锁。从pthread_cond_wait开始执行到其调用线程被放入条件变量的等待队列之间的这段时间内,pthread_cond_signal和pthread_cond_broadcast等函数不会修改条件变量的值。也就是说,当线程在进出条件变量的等待队列的时候是不会去唤醒等待队列中的其他线程的。当pthread_cond_wait函数成功返回时,互斥锁mutex将再次被锁上,这是pthread_cond_wait函数内部做的事情,就是为了防止在唤醒某个线程的时候和其他线程进出等待队列发生冲突。如果有一个线程正在进入等待队列,这时想要唤醒某个线程时就需要加锁,就会加锁失败,要等正在进入等待队列的线程彻底进去之后解锁,然后才能加锁去唤醒想要唤醒的线程,然后再加锁,然后出等待队列的之后在进行解锁。

注意:

线程被唤醒,pthread_cond_wait函数返回时,解除阻塞并重新申请获取互斥锁。

3.pthread_cond_broadcast()唤醒全部在等待条件变量的线程

参数解释:

参数cond:条件变量

4.pthread_cond_signal()唤醒一个在等待条件变量的线程(随机唤醒一个)

参数解释:

参数cond:条件变量

5.pthread_cond_destroy()销毁条件变量

参数解释:

参数cond:条件变量

三、条件变量的使用

【例】有两个线程A和B在等待队列中,给一个数组buff,当从键盘上向buff中写入数据之后,就随机唤醒一个线程去读取buff中的内容并输出,当从键盘上向buff中写入"end"的时候,唤醒所有正在等待的线程,让所有线程退出。

上例中,条件变量就是用户从键盘输入的数据,线程A和线程B根据条件变量的情况被唤醒。

代码如下 :

c 复制代码
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<pthread.h>
#include<string.h>
#include<semaphore.h>

pthread_cond_t cond;
pthread_mutex_t mutex;

char buff[128]={0};

void* fun_a(void* arg)
{
    while(1)
    {
        //在线程A进入等待队列之前加锁,如果加锁成功,线程A进入等待队列,这时别的线程不可以进入等待队列
        pthread_mutex_lock(&mutex);

        //线程A进入等待队列中,线程B被阻塞,解锁,线程A被唤醒,加锁
        pthread_cond_wait(&cond,&mutex);

        //在线程A从等待队列出来之后,解锁,这时别的线程就可以进入等待队列
        pthread_mutex_unlock(&mutex);
        if(strncmp(buff,"end",3)==0)        
        {
            break;
        }
        else
        {
            printf("线程A输出:%s",buff);
        }

    }
    printf("线程A结束\n");
}

void* fun_b(void* arg)
{
    while(1)
    {
        //在线程B进入等待队列之前加锁,如果加锁成功,线程B进入等待队列,这时别的线程不可以进入等待队列
        pthread_mutex_lock(&mutex);

        //线程B进入等待队列中,线程B被阻塞,解锁,线程B被唤醒,加锁
        pthread_cond_wait(&cond,&mutex);

        //在线程B从等待队列出来之后,解锁,这时别的线程就可以进入等待队列
        pthread_mutex_unlock(&mutex);

        if(strncmp(buff,"end",3)==0)
        {
            break;
        }
        else
        {
            printf("线程B输出:%s",buff);
        }
    }
    printf("线程B结束\n");
}

int main()
{
    pthread_mutex_init(&mutex,NULL);
    pthread_cond_init(&cond,NULL);

    pthread_t id1,id2;
    pthread_create(&id1,NULL,fun_a,NULL);
    pthread_create(&id2,NULL,fun_b,NULL);

    while(1)
    {
        
        fgets(buff,128,stdin);

        if(strncmp(buff,"end",3)==0)
        {
            //唤醒所有
            pthread_cond_broadcast(&cond);
            break;
        }
        else
        {
            //随机唤醒一个
            pthread_cond_signal(&cond);
        }
    }

    pthread_join(id1,NULL);
    pthread_join(id2,NULL);

    pthread_mutex_destroy(&mutex);
    pthread_cond_destroy(&cond);

    exit(0);
}

运行结果:

相关推荐
马里奥Mario2 分钟前
拯救被日志拖垮的线程池:Logback异步化改造实战
java
myloveasuka4 分钟前
[Linux]内核态与用户态详解
linux
@BreCaspian8 分钟前
在HP暗影精灵Ubuntu20.04上修复IntelAX211Wi-Fi不可用的全过程记录——系统安装以后没有WIFI图标&无法使用无线网
linux
程序无bug9 分钟前
Spring 当中的Bean 作用域
java
小眼睛FPGA13 分钟前
【RK3568+PG2L50H开发板实验例程】Linux部分/FPGA dma_memcpy_demo 读写案例
linux·运维·科技·ai·fpga开发·gpu算力
weixin_4373982121 分钟前
转Go学习笔记
linux·服务器·开发语言·后端·架构·golang
RainbowSea28 分钟前
补充:问题:CORS ,前后端访问跨域问题
java·spring boot·spring
津津有味道30 分钟前
Qt C++串口SerialPort通讯发送指令读写NFC M1卡
linux·c++·qt·串口通信·serial·m1·nfc
RainbowSea30 分钟前
15. MySQL 多版本并发控制
java·sql·mysql
倔强的石头10638 分钟前
飞算JavaAI:重构软件开发范式的智能引擎
java·数据库·重构