0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

详谈Linux操作系统编程的条件变量

如意 来源:良许Linux 作者:良许 2020-09-27 15:23 次阅读

条件变量是用来等待线程而不是上锁的,条件变量通常和互斥锁一起使用。条件变量之所以要和互斥锁一起使用,主要是因为互斥锁的一个明显的特点就是它只有两种状态:锁定和非锁定,而条件变量可以通过允许线程阻塞和等待另一个线程发送信号来弥补互斥锁的不足,所以互斥锁和条件变量通常一起使用。

当条件满足的时候,线程通常解锁并等待该条件发生变化,一旦另一个线程修改了环境变量,就会通知相应的环境变量唤醒一个或者多个被这个条件变量阻塞的线程。这些被唤醒的线程将重新上锁,并测试条件是否满足。一般来说条件变量被用于线程间的同步;当条件不满足的时候,允许其中的一个执行流挂起和等待。

简而言之,条件变量本身不是锁,但它也可以造成线程阻塞,通常与互斥锁配合使用,给多线程提供一个会合的场所。

条件变量的优点:

相较于mutex而言,条件变量可以减少竞争。如果仅仅是mutex,那么,不管共享资源里有没数据,生产者及所有消费都全一窝蜂的去抢锁,会造成资源的浪费。

如直接使用mutex,除了生产者、消费者之间要竞争互斥量以外,消费者之间也需要竞争互斥量,但如果汇聚(链表)中没有数据,消费者之间竞争互斥锁是无意义的。有了条件变量机制以后,只有生产者完成生产,才会引起消费者之间的竞争。提高了程序效率。

主要应用函数:

pthread_cond_init函数

pthread_cond_destroy函数

pthread_cond_wait函数

pthread_cond_timedwait函数

pthread_cond_signal函数

pthread_cond_broadcast函数

以上6 个函数的返回值都是:成功返回0, 失败直接返回错误号。

pthread_cond_t类型:用于定义条件变量,比如:pthread_cond_t cond;

##pthread_cond_init函数

函数原型:

int pthread_cond_init(pthread_cond_t restrict cond, const pthread_condattr_t restrict attr);

函数作用:初始化一个条件变量

参数说明:

cond:条件变量,调用时应传&cond给该函数

attr:条件变量属性,通常传NULL,表示使用默认属性

也可以使用静态初始化的方法,初始化条件变量:

pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

##pthread_cond_destroy函数

函数原型:

int pthread_cond_destroy(pthread_cond_t *cond);

函数作用:销毁一个条件变量

##pthread_cond_wait函数

函数原型:

int pthread_cond_wait(pthread_cond_t restrict cond, pthread_mutex_t restrict mutex);

函数作用:

阻塞等待一个条件变量。具体而言有以下三个作用:

阻塞等待条件变量cond(参1)满足;

释放已掌握的互斥锁mutex(解锁互斥量)相当于pthread_mutex_unlock(&mutex);

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

其中1、2.两步为一个原子操作。

##pthread_cond_timedwait函数

函数原型:

int pthread_cond_timedwait(pthread_cond_t restrict cond, pthread_mutex_t restrict mutex, const struct timespec *restrict abstime);

函数作用:限时等待一个条件变量

参数说明:

前两个比较好理解,重点说明第三个参数。

这里有个struct timespec结构体,可以在man sem_timedwait中查看。结构体原型如下:

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

struct timespec定义的形参abstime是个绝对时间。注意,是绝对时间,不是相对时间。什么是绝对时间?2018年10月1日10:10:00,这就是一个绝对时间。什么是相对时间?给洗衣机定时30分钟洗衣服,就是一个相对时间,也就是说从当时时间开始计算30分钟,诸如此类。

如:time(NULL)返回的就是绝对时间。而alarm(1)是相对时间,相对当前时间定时1秒钟。

adstime所相对的时间是相对于1970年1月1日00:00:00,也就是UNIX计时元年。

下面给出一个错误用法: struct timespec t = {1, 0}; pthread_cond_timedwait (&cond, &mutex, &t); 这种用法只能定时到 1970年1月1日 00:00:01秒,想必这个时间大家都还没出生。

正确用法: time_t cur = time(NULL); 获取当前时间。 struct timespec t; 定义timespec 结构体变量t t.tv_sec = cur+1; 定时1秒 pthread_cond_timedwait (&cond, &mutex, &t); 传参

##pthread_cond_signal函数

函数原型:

int pthread_cond_signal(pthread_cond_t *cond);

函数作用: 唤醒至少一个阻塞在条件变量上的线程

##pthread_cond_broadcast函数

函数原型:

int pthread_cond_broadcast(pthread_cond_t *cond);

函数作用: 唤醒全部阻塞在条件变量上的线程

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

不管是什么语言,只要提到线程同步,一个典型的案例就是生产者消费者模型。在Linux环境下,借助条件变量来实现这一模型,是比较常见的一种方法。

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

看如下示例,使用条件变量模拟生产者、消费者问题:

#include 《stdio.h》 #include 《stdlib.h》#include 《unistd.h》#include 《pthread.h》typedef struct msg { struct msg *next; int num;}msg_t;msg_t *head = NULL;msg_t *mp = NULL;/* 静态初始化 一个条件变量 和 一个互斥量*/pthread_cond_t has_product = PTHREAD_COND_INITIALIZER;pthread_mutex_t mutex =PTHREAD_MUTEX_INITIALIZER;void *th_producer(void *arg) { while (1) { mp = malloc(sizeof(msg_t)); mp-》num = rand() % 1000; //模拟生产一个产品 printf(“--- produce: %d --------\n”, mp-》num); pthread_mutex_lock(&mutex); mp-》next = head; head = mp; pthread_mutex_unlock(&mutex); pthread_cond_signal(&has_product); //唤醒线程去消费产品 sleep(rand() % 5); } return NULL; }void *th_consumer(void *arg) { while (1) { pthread_mutex_lock(&mutex); while (head == NULL) { //如果链表里没有产品,就没有抢锁的必要,一直阻塞等待 pthread_cond_wait(&has_product, &mutex); } mp = head; head = mp-》next; //模拟消费掉一个产品 pthread_mutex_unlock(&mutex); printf(“========= consume: %d ======\n”, mp-》num); free(mp); mp = NULL; sleep(rand() % 5); } return NULL; }int main(){ pthread_t pid, cid; srand(time(NULL)); pthread_create(&pid, NULL, th_producer, NULL); pthread_create(&cid, NULL, th_consumer, NULL); pthread_join(pid, NULL); pthread_join(cid, NULL); return 0; }

运行结果:

详谈Linux操作系统编程的条件变量


责编AJX

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • Linux
    +关注

    关注

    87

    文章

    10975

    浏览量

    206671
  • 变量
    +关注

    关注

    0

    文章

    593

    浏览量

    28103
  • 操作环境
    +关注

    关注

    0

    文章

    9

    浏览量

    7037
收藏 人收藏

    评论

    相关推荐

    Linux系统编程》书籍

    Linux系统编程》书籍分享,带详细目录
    发表于 12-10 12:00

    Linux系统编程

    Linux系统编程
    发表于 03-03 10:26

    Linux操作系统安装与使用

    嵌入式网络编程+嵌入式Linux系统一、Linux操作系统安装与使用(命令)二、Linux开发工
    发表于 11-05 08:25

    为何将Linux操作系统划分为用户和内核

    linux_C网络编程概述嵌入式网络编程==嵌入式Linux_C系统编程(文件、进程、进程间通信、多线程、网络、大并发网络服务器、数据库、s
    发表于 12-15 08:35

    Linux操作系统安装与使用

    嵌入式网络编程+嵌入式Linux系统Linux操作系统安装与使用(命令)Linux开发工具(gc
    发表于 12-22 07:47

    嵌入式Linux C系统编程相关资料下载

    D3.31、嵌入式网络编程==嵌入式Linux C系统编程(文件、进程、进程间通信、多线程、网络、大并发网络服务器、数据库、shell编程)2、嵌入式C开发VS嵌入式
    发表于 12-22 07:44

    Linux操作系统安装与使用方法

    嵌入式网络编程+嵌入式Linux系统Linux操作系统安装与使用(命令)Linux开发工具(gc
    发表于 12-23 06:57

    什么是Linux系统编程

    什么是Linux系统编程Linux系统编程也叫Linux下的高级编程,是介于应用层和驱动层之间
    发表于 12-23 07:30

    是否可以为linux操作系统编译并在基于debian的rasperry操作系统中运行?

    STM32CubeProgrammer 有一个 C++ API,允许创建使用 qt creator 编译的程序。是否可以为 linux 操作系统编译并在基于 debian 的 rasperry 操作系统中运行?
    发表于 12-06 06:01

    Linux操作系统

    linux的教学内容1 、Linux概述 2 、Linux操作系统安装3、 Linux的内核 4 、Li
    发表于 04-10 16:54 0次下载
    <b class='flag-5'>Linux</b><b class='flag-5'>操作系统</b>

    基于Linux操作系统下C语言编程入门

    基于Linux操作系统下C语言编程入门
    发表于 10-27 15:36 11次下载
    基于<b class='flag-5'>Linux</b><b class='flag-5'>操作系统</b>下C语言<b class='flag-5'>编程</b>入门

    linux系统编程_中文版

    linux系统编程_中文版
    发表于 10-29 10:17 0次下载
    <b class='flag-5'>linux</b><b class='flag-5'>系统编程</b>_中文版

    Linux操作系统下C语言编程入门.pdf

    Linux操作系统下C语言编程入门
    发表于 05-17 10:08 92次下载

    为什么我们要掌握Linux系统编程

    为什么我们要掌握Linux系统编程
    的头像 发表于 05-17 10:00 607次阅读

    Linux 系统编程的难点是什么

    对于有一定 Linux 开发基础希望进阶学习 Linux 系统编程的开发人员来说,其难点在于,Linux 所囊括的技术点繁杂,往往不知从何下手。
    发表于 06-28 12:54 385次阅读