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

运行结果:

相关推荐
MacroZheng3 分钟前
横空出世!MyBatis-Plus 同款 ES ORM 框架,用起来够优雅!
java·后端·elasticsearch
用户03321266636744 分钟前
Java 查找并替换 Excel 中的数据:详细教程
java
间彧1 小时前
ThreadLocal实现原理与应用实践
java
LH_R1 小时前
OneTerm开源堡垒机实战(四):访问授权与安全管控
运维·后端·安全
若水不如远方1 小时前
Netty的四种零拷贝机制:深入原理与实战指南
java·netty
用户7493636848431 小时前
【开箱即用】一分钟使用java对接海外大模型gpt等对话模型,实现打字机效果
java
用户31187945592181 小时前
Kylin Linux 10 安装 glib2-devel-2.62.5-7.ky10.x86_64.rpm 方法(附安装包)
linux
Raymond运维1 小时前
MariaDB源码编译安装(二)
运维·数据库·mariadb
SimonKing1 小时前
一键开启!Spring Boot 的这些「魔法开关」@Enable*,你用对了吗?
java·后端·程序员
涛啊涛2 小时前
Centos7非LVM根分区容量不足后扩容,对调硬盘挂载/
linux·磁盘管理