linux 下 pthread_cond_t 详解

2023-05-16

一览

  • 本文目的
  • 为何需要条件变量
  • 三个问题
    • 传入前锁mutex
    • 传入后解锁mutex
    • 返回前再次锁mutex
      • pthread_cond_signal的两种写法
  • 尾语

 


本文目的

 
  首先说明,本文重点不在怎么用条件变量。这里我先列出 apue 中对于pthread_cond_wait函数的这么一段话:

  • “ 调用者把锁住的互斥量传给函数,函数然后自动把调用线程放到等待条件的线程列表上,**对互斥量解锁。**这就关闭了条件检查和线程进入休眠状态等待条件改变这两个操作之间的时间通道,这样线程就不会错过条件的任何变化。pthread_cond_wait返回时,互斥量再次被锁住。

  这段话的信息量很大,其中关于互斥量的操作可以理解为以下三个点:

  1. 调用pthread_cond_wait前需要先对互斥量mutex上锁,才能把&mutex传入pthread_cond_wait函数
  2. 在pthread_cond_wait函数内部,会首先对传入的mutex解锁
  3. 当等待的条件到来后,pthread_cond_wait函数内部在返回前会去锁住传入的mutex

  我当时看到这里,各种疑问,传入前为何要锁,传入后为何要释放,返回时又为何再次锁?

本文就这三个问题进行详细解释。不过在此之前,我们需要了解为什么要有条件变量。即条件变量的作用。


为何需要条件变量

 
  如果没有条件变量,那么我们等待一个条件满足则会是下面这样的模型:
在这里插入图片描述
  首先加锁进入临界区去查看条件是否满足,不满足则解锁离开临界区,睡眠一段时间再继续循环判断。在这种情况下如果刚离开临界区,条件变为满足,那么线程必须还要等一段时间重新进入临界区才能知道条件满足(如果在这段时间内,条件依旧一直保持满足的话),如果这一小段时间条件又变为不满足,那么这个线程还要继续循环判断。不断地加锁解锁(会影响使用同一把锁的其他线程),还不能第一时间收到条件满足。这种模型既费时又开销大。

  所以条件变量的产生,正是为了不循环加锁解锁,并且第一时间收到条件满足的通知。


三个问题

 
  要回答那三个问题,那么首先需要明白 等待与唤醒的配合。

  下图是我参考其他人的图(原图有误)更正后所画的。其实这个图就能解释那三个问题:pthread_cond_wait传入前为何要锁,传入后为何先解锁,以及返回前为何再锁。不过我还是详细解释一下。
在这里插入图片描述
  图中有一个关键点,就是判断条件是否满足,是在调用pthread_cond_wait之前,上锁之后,就是说pthread_cond_wait不具备判断条件的能力,需要我们在外部写判断语句。

  1. 条件不满足时,才会进入pthread_cond_wait
  2. 进入pthread_cond_wait先解锁就马上阻塞
  3. pthread_cond_signal唤醒的是阻塞在pthread_cond_wait的进程

可以结合下面的代码会更清楚。

以下pthread_cond_wait和pthread_cond_signal的通常用法的伪代码(条件为:value是不是大于0):

	lock(&mutex);
	while(value<=0)//需要value>0所以 value<=0就条件不满足
	{
		pthread_cond_wait(&cond,&mutex);
	}
	unlock(&mutex);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
	lock(&mutex);
	if(value==0)
	{
		value++;
	}
	if(value>0)
	{
		pthread_cond_signal(&cond);
	}
	unlock(&mutex);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

把这个基本流程弄清楚后,就可以解释那三个问题了。


传入前锁mutex

在这里插入图片描述
  为了方便大家观看,每个问题的解释我都会再次把上图贴出。

传入前锁mutex是为了保证线程从条件判断到进入pthread_cond_wait前,条件不被改变。

  如果没有传入前的锁。就会有这样的情况:线程A判断条件不满足之后,调用pthread_cond_wait之前,A因为休眠,或者因为多线程下,多个线程执行顺序和快慢的因素,令线程B更改了条件,使得条件满足。但此时线程A还没有调用pthread_cond_wait。等到线程A又启动调用pthread_cond_wait后虽然条件满足,但却收不到pthread_cond_signal的唤醒,就一直阻塞下去。


传入后解锁mutex

在这里插入图片描述
传入后解锁是为了条件能够被改变

  传入后的解锁,是因为调用pthread_cond_signal的那部分,需要先加锁更改条件后才调用pthread_cond_signal。(更改条件与等待条件满足,都是针对条件这一个资源的竞争,所以调用pthread_cond_wait和调用pthread_cond_signal的两个线程需要同一把锁)

  如果pthread_cond_wait内不对mutex解锁,那么在调用pthread_cond_wait后,其他线程就不能更改条件,条件就会一直不满足。

返回前再次锁mutex

在这里插入图片描述

  1. 返回前再次锁mutex是为了保证线程从pthread_cond_wait返回后 到 再次条件判断前不被改变。
  2. 保证 在pthread_cond_signal之后与解锁mutex之间可能需要的其他语句能够执行

  对于1,这里的理由与传入pthread_cond_wait前锁mutex的理由差不多。如果不锁,那么线程A调用pthread_cond_wait后,条件满足,线程A被唤醒,从pthread_cond_wait返回。线程B在此时更改了条件,使得条件不满足。线程A并不知道条件又被更改,还是以为条件满足,就可能出错。

  对于2,只要在pthread_cond_signal之后与解锁mutex之间有其他语句需要执行,那么由于mutex在这时已经被这个线程锁,还没有解锁,所以调用pthread_cond_wait的那个线程在pthread_cond_wait返回前的锁mutex的行为就会阻塞,直到pthread_cond_signal后的语句执行完解锁,pthread_cond_wait才会返回。

说到这里就顺便说一下,由于pthread_cond_wait返回再次锁的行为,pthread_cond_signal不一定放在 lock()和unlock()中间。

pthread_cond_signal的两种写法

lock(&mutex);
//一些操作
pthread_cond_signal(&cond);
//一些操作
unlock(&mutex);
  • 1
  • 2
  • 3
  • 4
  • 5

  缺点:在某些线程的实现中,会造成等待线程从内核中唤醒(由于cond_signal)回到用户空间,然后pthread_cond_wait返回前需要加锁,但是发现锁没有被释放,又回到内核空间所以一来一回会有性能的问题。
  但是在LinuxThreads或者NPTL里面,就不会有这个问题,因为在Linux 线程中,有两个队列,分别是cond_wait队列和mutex_lock队列, cond_signal只是让线程从cond_wait队列移到mutex_lock队列,而不用返回到用户空间,不会有性能的损耗。所以Linux中这样用没问题。
 
 

lock(&mutex);
//一些操作
unlock(&mutex);
pthread_cond_signal(&cond);
  • 1
  • 2
  • 3
  • 4

优点:不会出现之前说的那个潜在的性能损耗,因为在signal之前就已经释放锁了

缺点:如果unlock之后signal之前,发生进程交换,另一个进程(不是等待条件的进程)拿到这把梦寐以求的锁后加锁操作,那么等最终切换到等待条件的线程时锁被别人拿去还没归还,只能继续等待。


尾语

  条件变量还是应多使用一下,才会有更清晰的理解。只看API还是行不通的。

以上

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

linux 下 pthread_cond_t 详解 的相关文章

  • 为 Qt 应用程序创建 Linux 安装

    我刚刚用 Qt Creator 制作了一个很棒的程序 我对自己很满意 如何将其从台式机移至笔记本电脑 那么 最好的方法是安装程序 对吗 对于 Ubuntu 这是一个 Debian 软件包 对吗 我怎么做 有人这样做过吗 他们可以分享 QT
  • 在 Ubuntu 16.04 上找不到 printf.c

    我最近切换到Ubuntu 16 04 我在用vscode作为 Ubuntu 上的 IDE 我配置了其他语言 但我无法做到这一点C C 我创建c cpp properties json launch json tasks json 当我开始编
  • sleep 0 有特殊含义吗?

    我看到很多用法sleep 0在我的一个客户项目中 代码看起来像这样 while true sleep 0 end 阅读一些像这样的答案this https stackoverflow com questions 3727420 signif
  • 适用于 KDE 和 Gnome 的 Gui [重复]

    这个问题在这里已经有答案了 我想为一个现在是 CLI 的应用程序编写一个 gui 它需要在 KDE 和 Gnome DE 中 看起来不错 充分利用用户的外观设置 如果我选择 Qt 或 GTK 我能够做到这一点吗 它们与两个 DE 集成良好吗
  • C 程序从连接到系统的 USB 设备读取数据

    我正在尝试从连接到系统 USB 端口的 USB 设备 例如随身碟 获取数据 在这里 我可以打开设备文件并读取一些随机原始数据 但我想获取像 minicom teraterm 这样的数据 请让我知道我可以使用哪些方法和库来成功完成此操作以及如
  • 劫持系统调用

    我正在编写一个内核模块 我需要劫持 包装一些系统调用 我正在暴力破解 sys call table 地址 并使用 cr0 来禁用 启用页面保护 到目前为止一切顺利 一旦完成 我将公开整个代码 因此如果有人愿意 我可以更新这个问题 无论如何
  • Linux 桌面快捷方式和安装图标

    我需要添加什么到我的 spec文件来创建桌面快捷方式并在安装过程中为快捷方式分配一个图标 rpm 如果需要脚本 一个示例将非常有帮助 您在 Linux 下使用 desktop 文件作为图标 图标放置的位置取决于您使用的发行版和桌面环境 由于
  • 没有可用的符号表信息

    我正在测试第三方的库 它崩溃了 当我想查看崩溃的原因时 我的 gdb 告诉我没有可用的调试符号 Program received signal SIGSEGV Segmentation fault Switching to Thread 0
  • 在主目录中安装库

    在 Linux Ubuntu 中 我尝试运行一个工具 但它显示错误 库丢失 我无权在系统中安装任何内容 或者根本无法从我的用户帐户执行 sudo 是否可以在我的主目录 没有 sudo 中安装缺少的库 在我的例子中为 libstdc so 6
  • diff 文件仅比较每行的前 n 个字符

    我有2个文件 我们将它们称为 md5s1 txt 和 md5s2 txt 两者都包含a的输出 find type f print0 xargs 0 md5sum sort gt md5s txt 不同目录下的命令 许多文件被重命名 但内容保
  • 如何更改 Ubuntu 14.04 上的 php-cli 版本?

    我是 Linux 新手 在篡改时破坏了一些 php 设置 如果我执行一个包含以下内容的 php 脚本 phpinfo 它显示 php 版本为 5 6 但通过命令行 如果我运行php v它返回 7 0 版本 我想让两个版本匹配 我怎样才能修复
  • 嵌入式Linux poll()不断返回

    我有一个特别的问题 当我知道没有什么可读时 民意调查不断返回 因此设置如下 我有 2 个文件描述符 它们构成fd设置民意调查监视 一种用于引脚从高到低的变化 GPIO 另一个用于代理输入 代理输入出现问题 处理的顺序是 启动main函数 然
  • 何时使用 pthread 条件变量?

    线程问题 看来 只有在其他线程调用 pthread cond notify 之前调用 pthread cond wait 时 条件变量才起作用 如果在等待之前发生通知 那么等待将被卡住 我的问题是 什么时候应该使用条件变量 调度程序可以抢占
  • 修改linux下的路径

    虽然我认为我已经接近 Linux 专业人士 但显然我仍然是一个初学者 当我登录服务器时 我需要使用最新版本的R 统计软件 R 安装在 2 个地方 当我运行以下命令时 which R I get usr bin R 进而 R version
  • bluetoothctl 到 hcitool 等效命令

    在 Linux 中 我曾经使用 hidd connect mmac 来连接 BT 设备 但自 Bluez5 以来 这种情况已经消失了 我可以使用 bluetoothctl 手动建立连接 但我需要从我的应用程序使用这些命令 并且使用 blue
  • 为什么我收到“无法进行二进制日志记录”的信息。在我的 MySQL 服务器上?

    当我今天启动 MySQL 服务器并尝试使用以下命令进行一些更改时用于 MySQL 的 Toad http www quest com toad for mysql 我收到此消息 MySQL 数据库错误 无法进行二进制日志记录 消息 交易级别
  • 无法加载 JavaHL 库。- linux/eclipse

    在尝试安装 Subversion 插件时 当 Eclipse 启动时出现此错误 Failed to load JavaHL Library These are the errors that were encountered no libs
  • 通过特定分隔符删除字符串

    我的文件中有几列 其中第二列有 分隔符 我想删除第二列中的第一个 第三个和第四个字符串 并将第二个字符串留在该列中 但我有正常的分隔符空间 所以我不知道 input 22 16050075 A G 16050075 A G 22 16050
  • 如何使用 bash 锁定文件

    我有一个任务从远程服务器同步目录 rsync av email protected cdn cgi l email protection srv data srv data 为了使其定期运行并避免脚本 reEnter 问题 我使用 rsyn
  • gdb查找行号的内存地址

    假设我已将 gdb 附加到一个进程 并且在其内存布局中有一个文件和行号 我想要其内存地址 如何获取文件x中第n行的内存地址 这是在 Linux x86 上 gdb info line test c 56 Line 56 of test c

随机推荐