Linux网络编程【条件变量】

举报
xcc-2022 发表于 2022/10/24 21:02:11 2022/10/24
1.1k+ 0 0
【摘要】 05. 条件变量 5.1 条件变量概述与互斥锁不同,条件变量是用来等待而不是用来上锁的,条件变量本身不是锁!条件变量用来自动阻塞一个线程,直到某特殊情况发生为止。通常条件变量和互斥锁同时使用。条件变量的两个动作:条件不满, 阻塞线程当条件满足, 通知阻塞的线程开始工作条件变量的类型: pthread_cond_t。 5.2 pthread_cond_init函数#include <pthr...

05. 条件变量

5.1 条件变量概述

与互斥锁不同,条件变量是用来等待而不是用来上锁的,条件变量本身不是锁

条件变量用来自动阻塞一个线程,直到某特殊情况发生为止。通常条件变量和互斥锁同时使用。

条件变量的两个动作:

  • 条件不满, 阻塞线程
  • 当条件满足, 通知阻塞的线程开始工作

条件变量的类型: pthread_cond_t。

5.2 pthread_cond_init函数

#include <pthread.h>

int pthread_cond_init(pthread_cond_t *restrict cond,
    const pthread_condattr_t *restrict attr);
功能:
    初始化一个条件变量
参数:
    cond:指向要初始化的条件变量指针。
    attr:条件变量属性,通常为默认值,传NULL即可
        也可以使用静态初始化的方法,初始化条件变量:
        pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
返回值:
    成功:0
    失败:非0错误号

5.3 pthread_cond_destroy函数

#include <pthread.h>

int pthread_cond_destroy(pthread_cond_t *cond);
功能:
    销毁一个条件变量
参数:
    cond:指向要初始化的条件变量指针
返回值:
    成功:0
    失败:非0错误号

5.4 pthread_cond_wait函数

#include <pthread.h>

int pthread_cond_wait(pthread_cond_t *restrict cond,
    pthread_mutex_t *restrict mutex);
功能:
    阻塞等待一个条件变量
    a) 阻塞等待条件变量cond(参1)满足
    b) 释放已掌握的互斥锁(解锁互斥量)相当于pthread_mutex_unlock(&mutex);
            a) b) 两步为一个原子操作。
    c) 当被唤醒,pthread_cond_wait函数返回时,解除阻塞并重新申请获取互斥锁pthread_mutex_lock(&mutex);

参数:
    cond:指向要初始化的条件变量指针
    mutex:互斥锁

返回值:
    成功:0
  
int pthread_cond_timedwait(pthread_cond_t *restrict cond,
    pthread_mutex_t *restrict mutex,
    const struct
                           .*restrict abstime);
功能:
    限时等待一个条件变量

参数:
    cond:指向要初始化的条件变量指针
    mutex:互斥锁
    abstime:绝对时间

返回值:
    成功:0
    失败:非0错误号

abstime补充说明:

struct timespec {
    time_t tv_sec;      /* seconds */ // 秒
    long   tv_nsec; /* nanosecondes*/ // 纳秒
}

time_t cur = time(NULL);        //获取当前时间。
struct timespec t;              //定义timespec 结构体变量t
t.tv_sec = cur + 1;             // 定时1秒
pthread_cond_timedwait(&cond, &t);

5.5 pthread_cond_signal函数

唤醒至阻塞在条件变量上的线程

#include <pthread.h>

int pthread_cond_signal(pthread_cond_t *cond);
功能:
    唤醒至少一个阻塞在条件变量上的线程
参数:
    cond:指向要初始化的条件变量指针
返回值:
    成功:0
    失败:非0错误号

int pthread_cond_broadcast(pthread_cond_t *cond);
功能:
    唤醒全部阻塞在条件变量上的线程
参数:
    cond:指向要初始化的条件变量指针
返回值:
    成功:0
    失败:非0错误号

5.6条件变量应用示例

//每隔两秒钟修改条件
int flag = 0;//判断条件标志
pthread_cond_t cond;//互斥量
pthread_mutex_t mutex;//条件变量
//改变条件的线程
void* fun1(void *arg)
{
    while(1)
    {
        //加锁
        pthread_mutex_lock(&mutex);
        flag = 1;
        //解锁
        pthread_mutex_unlock(&mutex);

        //唤醒因为条件而阻塞线程
        pthread_cond_signal(&cond);
        sleep(2);
    }

    return NULL;
}
//等待条件的线程
void* fun2(void* arg)
{ 
 
    while(1)
    {
        //加锁
        pthread_mutex_lock(&mutex);
        //表示条件不满足
        if(0 == flag)
        {
            //等待条件满足 会阻塞
            printf("条件暂时不满足,需等待一会...\n");
            pthread_cond_wait(&cond,&mutex);
        }
        printf("线程2因为满足条件 开始运行...\n");
        flag = 0;

        //解锁
        pthread_mutex_unlock(&mutex);
    }

    return NULL;
}
int main()
{
    pthread_t tid1,tid2;;
    int ret = -1;

    //初始化互斥锁
    ret = pthread_mutex_init(&mutex,NULL);
    if(0 != ret)
    {
        printf("pthread_mutex_init failed...\n");
        return 1;
    }

    //初始化条件变量
    ret = pthread_cond_init(&cond,NULL);
    if(0 != ret)
    {
        printf("pthread_cond_init failed...\n");
        return 1;
    }

    //创建两个线程
    ret = pthread_create(&tid1,NULL,fun1,NULL);
    if(0 != ret)
    {
        printf("pthread_create failed...\n");
        return 1;
    }
    ret = pthread_create(&tid2,NULL,fun2,NULL);
    if(0 != ret)
    {
        printf("pthread_create failed...\n");
        return 1;
    }

    //回收两个线程资源
    pthread_join(tid1,NULL);
    pthread_join(tid2,NULL);

    //销毁
    pthread_mutex_destroy(&mutex);
    pthread_cond_destroy(&cond);

    return 0;
}

动画10

5.6 生产者消费者条件变量模型

线程同步典型的案例即为生产者消费者模型,而借助条件变量来实现这一模型,是比较常见的一种方法。

假定有两个线程,一个模拟生产者行为,一个模拟消费者行为。两个线程同时操作一个共享资源(一般称之为汇聚),生产向其中添加产品,消费者从中消费掉产品。

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

//生产者与消费者模型
//连表节点类型
typedef struct _node_t
{
    int date;               //数据类
    struct _node_t* next;   //指针域

}node_t;

 node_t* head = NULL;       //头节点
pthread_mutex_t mutex;      //互斥量
pthread_cond_t cond;       //条件变量

//生产者
void* procuder(void *arg)
{
    node_t* new = NULL;     //新节点
    //加锁
    pthread_mutex_lock(&mutex);
    //循环生产产品
    while(1)
    {
        
        node_t* new = malloc(sizeof(node_t));   //分配节点空间(使用堆会出现不可重入问题,需要使用锁)

        if(new == NULL)
        {
            printf("malloc failed.../n");
            break;
        }
        memset(new,0,sizeof(node_t));
        new->date = random()%100+1;//赋值:1-100
        new->next = NULL;
        printf("生产者生产产品:%d\n",new->date);

        //头插法
        new->next = head;
        head = new;
        
        pthread_mutex_unlock(&mutex); //解锁

        //唤醒因为i条件变量而阻塞的线程
        pthread_cond_signal(&cond);
        
        sleep(random()%3+1);//随机睡眠1-3秒

    }
    pthread_exit(NULL);

}
//消费者线程
void *customer(void* arg)
{
    node_t* tmp = NULL;         //临时节点
   
    pthread_mutex_lock(&mutex); //加锁
    //循环消费
    while(1)
    {
        //链表为空的情形
        if(head == NULL)
        {
            //如果链表为空 就阻塞
            pthread_cond_wait(&cond,&mutex);
        }
        else
        {
            //第一个节点地址赋值给临时变量tmp
           tmp = head;
           //head指向链表的第二个节点
           head = head->next;

           printf("消费者消耗产品:%d\n",tmp->date);
           free(tmp);       //释放空间
           
           pthread_mutex_unlock(&mutex);    //解锁
            sleep(random()%3+1);//随机睡眠1-3秒

        }
    }

    return NULL;
}
//生产者和消费者模型 条件变量的模型
int main(void)
{

    int ret = -1;
    srandom(getpid());  //设置随机种子
    pthread_t tid1 = -1, tid2 = -1;
    
    //初始化互斥锁
    ret = pthread_mutex_init(&mutex,NULL);
    if(0 != ret)
    {
        printf("pthread_mutex_init falied...\n");
    }
    //初始化条件变量
    ret = pthread_cond_init(&cond,NULL);
    if(0 != ret)
    {
        printf("pthread_cond_init falied...\n");
    }

    //创建两个线程
    ret =  pthread_create(&tid1,NULL,procuder,NULL);
    if(0 != ret)
    {
        printf("pthread_create failed...\n");
        return 1;
    }

    ret =  pthread_create(&tid2,NULL,customer,NULL);
    if(0 != ret)
    {
        printf("pthread_create failed...\n");
        return 1;
    }

    //等待两个线程结束,回收资源
    pthread_join(tid1,NULL);
    pthread_join(tid2,NULL);

    //销毁互斥锁 
    pthread_mutex_destroy(&mutex);
    //销毁条件变量
    pthread_cond_destroy(&cond);
    return 0;
}

image-20220927222140494

image-20220927222201349

运行结果:

xcc@ubuntu:~/9th$ ./a.out
生产者生产产品:43
消费者消耗产品:43
生产者生产产品:51
消费者消耗产品:51
生产者生产产品:97
消费者消耗产品:97
生产者生产产品:94
消费者消耗产品:94
生产者生产产品:89
生产者生产产品:14
消费者消耗产品:14
消费者消耗产品:89
生产者生产产品:35
消费者消耗产品:35
生产者生产产品:76
消费者消耗产品:76
生产者生产产品:84
消费者消耗产品:84
生产者生产产品:87
消费者消耗产品:87
生产者生产产品:36
消费者消耗产品:36
生产者生产产品:89
消费者消耗产品:89
^C

5.7 条件变量的优缺点

相较于mutex而言,条件变量可以减少竞争。

如直接使用mutex,除了生产者、消费者之间要竞争互斥量以外,消费者之间也需要竞争互斥量,但如果汇聚(链表)中没有数据,消费者之间竞争互斥锁是无意义的。

有了条件变量机制以后,只有生产者完成生产,才会引起消费者之间的竞争。提高了程序效率。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

作者其他文章

评论(0

抱歉,系统识别当前为高风险访问,暂不支持该操作

    全部回复

    上滑加载中

    设置昵称

    在此一键设置昵称,即可参与社区互动!

    *长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

    *长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。