当前位置:首页 > 技术知识 > 正文内容

Linux系统编程:条件变量为什么要用锁

maynowei9个月前 (08-03)技术知识135

条件变量可以解决线程同步和共享资源访问的问题,条件变量是对互斥锁的补充,它允许一个线程阻塞并等待另一个线程发送的信号,当收到信号时,阻塞的线程被唤醒并试图锁定与之相关的互斥锁。具体定义如下:

等待:

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

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

唤醒:

int pthread_cond_signal(pthread_cond_t *cond);

int pthread_cond_broadcast(pthread_cond_t *cond);

具体使用场景:

在等待线程实现如下:

pthread_mutex_lock(&same_mutex);

pthread_cond_wait(&m_cond, &same_mutex);

pthread_mutex_unlock(&same_mutex);

在唤醒线程实现如下:

pthread_mutex_lock(&same_mutex);

pthread_cond_signal(&m_cond);

pthread_mutex_unlock(&same_mutex);

等待线程会在pthread_cond_wait()调用时挂起,并且在锁的保护下构造和检查好m_cond对应的条件链表后先解锁same_mutex,然后睡眠。这里解锁和睡眠是一个原子操作。当等待线程收到唤醒线程的通知后,pthread_cond_wait做的第一件事是把same_mutex重新加锁并唤醒返回,这里加锁和唤醒也是一个原子操作。

那么唤醒线程和等待线程为什么要用同一个mutex锁呢?首先,如果在等待线程调用了pthread_cond_wait()但是还没有开始等待(因为之前还要做链表的初始化工作),这时唤醒操作如果发生则会导致唤醒丢失(竞态条件)。因此必须使用锁防止条件链表这个共享资源被同时操作。(等待线程先在锁保护下操作完共享链表,然后解锁和睡眠是原子操作后开始睡眠,因此唤醒操作如果是在同一把锁保护中才能唤醒的话(唤醒也会修改共享链表),等待线程一定已经访问完共享资源并且睡眠了,就防止了冲突丢失唤醒信号)。 但是如果当前没有线程阻塞等待唤醒,pthread_cond_signal()函数调用后应没有任何效果。

因此条件变量使用锁是为了防止唤醒和等待操作同时发生的竞态条件,从而导致唤醒丢失甚至引起死锁。但是如果是认为不等待直接唤醒则该唤醒会直接丢失。

注:

最后pthread_cond_wait唤醒和加锁也是一个原子操作,如果有多个线程在同一个条件变量上等待,可以保证虽然都被唤醒了但是只有一个线程会获取到锁。

相关文章

C++11 同步机制:互斥锁和条件变量

前段时间,我研究了 ROS2(Jazzy)机器人开发系统,并将官网中比较重要的教程和概念,按照自己的学习顺序翻译成了中文,进行了整理和记录。到目前为止,已经整理了20多篇文章。如果你想回顾之前的内容,...

centos系统安装oracle简易客户端instantclient

一、目录1:centos 环境准备2:instant client 下载3:instant client 安装4:instant client 配置5:测试连接二、安装步骤1:zip,unzip命令安...

Oracle又双叕开始严查JDK,连夜提桶跑路

哈佛商业报道了最近几起北美的JDK许可问题公司用了来源不明的JDK,怕蹲里面,连夜提桶跑路The company uses JDK from unknown sources, and is afrai...

Oracle 不是有效的导出文件,标头验证失败 解决方法

第一种:网上搜索到的大多解决方法是说导出文件时使用的Oracle版本不一致问题,需要修改dmp文件的版本号。如果确定版本号确实不一样,请自行搜索一下解决方法。第二种:备份dmp文件时,备份的语句可能使...

Oracle数据库无法连接问题排查(oracle数据库连接不成功)

数据库告警日志 如下图 。发现 问题时间段,没有 数据库服务故障 报错,但是存在较多 TNS-12535 、 12560 、 12170 、 00505 错误:通过检查问题时间段应用日志, 也记录了...

超详细的Oracle19c修改数据库用户名教程

概述由于开发很多视图指定了某个用户名,故需修改数据库用户名srmpro为srm。以下为操作过程..1、停止应用防止修改用户名密码后应用一直在发起错误连接,可事先查询哪个IP在连接数据库,然后断开对应连...