当前位置: 首页 > web >正文

条件变量的基本介绍与有界缓冲区问题

条件变量

多数情况下,线程需要检查某一条件是否满足之后,才会继续运行。

线程可以使用条件变量来等待一个条件变成真。条件变量是一个显式队列,当某些条件不满足时线程可以把自己加入队列,等待该条件。另外某个线程改变了上述条件时,就可以唤醒一个或多个等待线程,让它们继续执行。

//该函数用于使线程睡眠
pthread_cond_wait(pthread_cond_t *c, pthread_mutex_t *m)
{//互斥量参数的意义://该函数假定在被调用时该互斥量是已上锁的状态,wait函数负责释放锁,并让//调用线程休眠。当线程被唤醒时,该函数必须重新获取锁,再返回给调用者//这样做的目的是为了避免在线程休眠时产生一些竟态条件
}//该函数用于唤醒线程
pthread_cond_signal(pthread_cond_t  *c)
{} //状态变量,标志着子线程是否完成
int done = 0; pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER; 
pthread_cond_t c = PTHREAD_COND_INITIALIZER; void thr_exit() {Pthread_mutex_lock(&m); done = 1; Pthread_cond_signal(&c); Pthread_mutex_unlock(&m); } void *child(void *arg) { printf("child\n"); //子线程退出并唤醒父线程thr_exit();return NULL; 
} void thr_join() { //获取锁Pthread_mutex_lock(&m); //检查子线程是否完成,未完成则使父线程休眠,这里的done变量是必须的//这里使用while循环比if好while (done == 0) Pthread_cond_wait(&c, &m);//此时子线程得以运行//从wait返回时持有锁,此时释放锁Pthread_mutex_unlock(&m); 
} int main(int argc, char *argv[]) { printf("parent: begin\n"); pthread_t p;//创建子线程Pthread_create(&p, NULL, child, NULL); //使子线程运行thr_join(); printf("parent: end\n"); return 0; 
}

如果没有done变量,假设子线程先运行,那么父线程就永远得不到运行。done变量记录了线程的状态,是必须的。

假设在join函数中没有互斥量,那么会产生一个竟态条件。父线程在子线程前运行,在即将调用wait函数时,发生了中断使子线程运行。子线程修改done为1,并发送信号使徒唤醒父线程,但是此时父线程在休眠之前被中断了,它并没有处于休眠状态,所以父线程永远不会被唤醒。

总结以下就是发送信号时总是持有锁。

生产者/消费者(有界缓冲区)问题

假设有一个或多个生产者线程和一个或多个消费者线程。生产者把生成的数据项放入缓冲区;消费者从缓冲区取走数据项,以某种方式消费。因为有界缓冲区是共享资源,所以我们必须通过同步机制来访问它,以免产生竞态条件。

//使用一个整形变量来代表缓冲区
int buffer; //该变量用于辨识缓冲区中是否有数据,0为空,1为满
int count = 0; // initially, empty void put(int value) { assert(count == 0); count = 1; buffer = value; 
} int get() { assert(count == 1); count = 0; return buffer; 
} 

发信号给线程只是唤醒它们,暗示状态发生了变化,但并不会保证在它运行之前状态一直是期望的情况。信号的这种释义常称为 Mesa 语义(Mesa semantic),为了纪念以这种方式建立条件变量的首次研究。另一种释义是 Hoare 语义(Hoare semantic),虽然实现难度大,但是会保证被唤醒线程立刻执行。实际上,几乎所有系统都采用了Mesa语义。

关于最终的有界缓冲区问题解决方案,是使用while、两个条件变量,并让生产者在缓冲区满时休眠,让消费者在缓冲区空时休眠。

http://www.xdnf.cn/news/17965.html

相关文章:

  • 异步开发:协程、线程、Unitask
  • C语言——深入理解指针(四)
  • 获取农历日期
  • Jeecg后端经验汇总
  • strings命令和findstr命令验证iso文件中ntkrnlmp.exe系统版本
  • 如何避免网盘中资源被和谐?
  • LeetCode算法日记 - Day 12: 寻找旋转排序数组中的最小值、点名
  • Erlang notes[2]
  • Vue 侦听器(watch 与 watchEffect)全解析1
  • 图解软件知识库体系
  • GaussDB 常用数值类型
  • 分布式锁:从理论到实战的深度指南
  • python自学笔记8 二维和三维可视化
  • 深入解析Prompt缓存机制:原理、优化与实践经验
  • 云原生俱乐部-杂谈1
  • CVE-2014-6271(bash破壳漏洞 )
  • Android数据缓存目录context.getCacheDir与Environment.getExternalStorageDirectory
  • Git 中切换到指定 tag
  • 会议系统进程池管理:初始化、通信与状态同步详解
  • Fiddler抓包
  • 【FreeRTOS】刨根问底4: 优先级反转是啥?咋解决?
  • 为什么Integer缓存-128 ~ 127
  • 学习设计模式《二十二》——职责链模式
  • 搭建 Docker 私有仓库
  • springboot项目不同平台项目通过http接口AES加密传输
  • UE5配置MRQ编解码器输出MP4视频
  • 机器人“ChatGPT 时刻”倒计时
  • 电池模组奇异值分解降阶模型
  • 两种方法实现,本地部署Qwen-Image,并开放API服务
  • MyBatis学习总结(六)