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

运行结果:

相关推荐
天天摸鱼的java工程师4 分钟前
CTO新项目直接上MySQL 8.0,老系统仍是5.7
java·后端·mysql
bxlj_jcj5 分钟前
解锁Java多级缓存:性能飞升的秘密武器
java·缓存·面试·架构
未来并未来8 分钟前
Redis 缓存问题及其解决方案
java·redis·缓存
一张假钞10 分钟前
Linux 下 ChromeDriver 安装
linux·运维·服务器
shark-chili13 分钟前
Java并发编程哲学系列汇总
linux·运维·服务器·操作系统
weixin_3077791317 分钟前
Neo4j 备份与恢复:原理、技术与最佳实践
运维·数据库·neo4j
快乐肚皮28 分钟前
MySQL集群模式详解:架构、优缺点与生产环境选型指南
java·mysql
qq_2430507929 分钟前
rtpmixsound:实现音频混音攻击!全参数详细教程!Kali Linux教程!
linux·web安全·网络安全·黑客·渗透测试·voip·kali linux
背太阳的牧羊人30 分钟前
docker 中 什么是「卷」?(Volume)
运维·docker·容器
Dxy123931021631 分钟前
DrissionPage 异常处理实战指南:构建稳健的网页自动化防线
运维·爬虫·python·自动化·drissionpage