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

Linux:线程同步

文章目录

  • 一、线程同步的基本认识
  • 二、条件变量的引入
    • 1. 为什么需要条件变量
    • 2. 条件变量与互斥量的关系
  • 三、条件变量的接口与使用
    • 1. 初始化与销毁
    • 2. 等待与唤醒
    • 3. 线程同步封装
  • 四、竞态条件与同步
    • 1. 什么是竞态条件
    • 2. 同步的本质
  • 五、总结


一、线程同步的基本认识

线程同步是多线程编程中非常重要的一部分,它的核心目标是保证数据的一致性和执行顺序的可控性。
在并发场景下,线程往往需要共享资源,而这种共享会带来竞争问题。如果不加以控制,就可能出现线程饥饿、竞态条件等现象,从而导致程序异常甚至崩溃。

同步的核心思路就是:让多个线程在访问临界资源时按照一定的顺序执行,保证数据的正确性和安全性。
其中比较常见的同步工具就是互斥量和条件变量。本篇博客主要围绕条件变量展开,帮助大家理解如何在合适的时机协调线程的执行顺序。

在这里插入图片描述


二、条件变量的引入

1. 为什么需要条件变量

当一个线程访问某个共享资源时,它可能会遇到资源暂时不可用的情况。例如,一个线程要访问队列,但队列为空,此时它只能等待,直到其他线程将数据放入队列中。
这种等待与唤醒机制,就是条件变量能够解决的问题。通过条件变量,线程可以在特定条件不满足时挂起自己,当条件被满足时再被唤醒继续执行。


2. 条件变量与互斥量的关系

条件变量通常与互斥量配合使用:

  • 互斥量用于保护共享资源,保证在临界区的访问安全。
  • 条件变量则用于在合适的时机唤醒等待中的线程,让它们继续工作。

因此,条件变量本质上是“在安全环境下的等待与唤醒机制”,二者必须搭配使用。


三、条件变量的接口与使用

1. 初始化与销毁

条件变量在使用前必须初始化,常见方法有两种:

静态初始化:

pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

动态初始化:

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

其中 attr 一般传 NULL

销毁条件变量:

int pthread_cond_destroy(pthread_cond_t *cond);

2. 等待与唤醒

线程等待条件满足时使用:

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

调用时需要传入条件变量和互斥量,线程会先释放互斥量并进入等待状态,直到被唤醒后再重新获得互斥量继续执行。

线程唤醒等待者有两种方式:

int pthread_cond_signal(pthread_cond_t *cond);      // 唤醒一个线程
int pthread_cond_broadcast(pthread_cond_t *cond);   // 唤醒所有等待线程

3. 线程同步封装

下面通过一个小例子演示条件变量的使用:

#include <iostream>
#include <vector>
#include <string>
#include <unistd.h>
#include <pthread.h>#define NUM 5
int cnt = 1000;pthread_mutex_t glock = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t gcond = PTHREAD_COND_INITIALIZER;// 等待是需要等,什么条件才会等呢?票数为0,等待之前,就要对资源的数量进行判定。
// 判定本身就是访问临界资源!,判断一定是在临界区内部的.
// 判定结果,也一定在临界资源内部。所以,条件不满足要休眠,一定是在临界区内休眠的!
// 证明一件事情:条件变量,可以允许线程等待
//                       可以允许一个线程唤醒在cond等待的其他线程, 实现同步过程
void *threadrun(void *args)
{std::string name = static_cast<const char *>(args);while (true){pthread_mutex_lock(&glock);// 直接让对用的线程进行等待?? 临界资源不满足导致我们等待的!pthread_cond_wait(&gcond, &glock); // glock在pthread_cond_wait之前,会被自动释放掉std::cout << name << " 计算: " << cnt << std::endl;cnt++;pthread_mutex_unlock(&glock);}
}int main()
{std::vector<pthread_t> threads;for (int i = 0; i < 5; i++){pthread_t tid;char* name = new char[64];snprintf(name, 64, "thread-%d", i);int n = pthread_create(&tid, nullptr, threadrun, name);if (n != 0)continue;threads.push_back(tid);sleep(1);}while (true){std::cout << "唤醒所有线程" << std::endl;pthread_cond_broadcast(&gcond);// std::cout << "唤醒一个线程... " << std::endl;// pthread_cond_signal(&gcond);sleep(1);}for (auto &id : threads){int m = pthread_join(id, nullptr);(void)m;}return 0;}

运行结果是:

thread-1 活动...
thread-2 活动...
thread-1 活动...
thread-1 活动...
thread-2 活动...

可以看到,条件变量让线程在满足条件时被唤醒,避免了忙等问题。


四、竞态条件与同步

1. 什么是竞态条件

竞态条件是由于线程执行的时序不确定,导致多个线程同时访问和修改共享资源,从而出现逻辑错误的现象。
例如两个线程几乎同时检查队列是否为空并准备取数据,但其中一个线程取走后另一个线程再取,就会发生错误。


2. 同步的本质

同步的核心是避免竞态条件,同时让线程按照正确的顺序访问临界资源。
通过互斥量和条件变量的配合,我们可以保证:

  • 共享资源访问有序,不会出现数据冲突;
  • 线程在条件不满足时进入等待,而不是不断轮询;
  • 在条件满足时,等待的线程能够被高效唤醒。

五、总结

条件变量是多线程同步机制中的重要组成部分,尤其适合“等待-唤醒”模式。它通常与互斥量配合使用,避免了无意义的忙等,让线程在条件满足时才继续执行,从而提升程序的效率与安全性。

掌握条件变量的使用,不仅能帮助我们写出更健壮的多线程代码,也为实现复杂的线程协作打下了基础。



文章转载自:

http://QWV8Gfvo.jbfzx.cn
http://9imZgM1o.jbfzx.cn
http://sDGDalfu.jbfzx.cn
http://LYSTAioD.jbfzx.cn
http://VOIwBaJX.jbfzx.cn
http://vSdDsMYJ.jbfzx.cn
http://SsCQyh3u.jbfzx.cn
http://i9O8mJZI.jbfzx.cn
http://ZOsmb2qO.jbfzx.cn
http://WhKGPdZI.jbfzx.cn
http://otsE4Ejq.jbfzx.cn
http://HklUPKS6.jbfzx.cn
http://0AHMhx1R.jbfzx.cn
http://q1sjUiie.jbfzx.cn
http://6GDUg9CU.jbfzx.cn
http://wo1jIeIS.jbfzx.cn
http://PWTwccJq.jbfzx.cn
http://u6hFKpSc.jbfzx.cn
http://waf9tP0T.jbfzx.cn
http://eX9l9bvk.jbfzx.cn
http://mpOvSIoe.jbfzx.cn
http://oOZ0SKMV.jbfzx.cn
http://rPI82UWV.jbfzx.cn
http://C6DV0mNc.jbfzx.cn
http://eVToaz9T.jbfzx.cn
http://NbzfEXYE.jbfzx.cn
http://bhQfbBEM.jbfzx.cn
http://XMJu6i3G.jbfzx.cn
http://4MVHK9bM.jbfzx.cn
http://3dnBzZ93.jbfzx.cn
http://www.dtcms.com/a/385697.html

相关文章:

  • Day24_【深度学习(3)—PyTorch使用(2)—张量的数值计算】
  • 9月15日
  • 【langchain】构建简单检索问答链
  • 简单的数组
  • ENVI系列教程(四)——图像几何校正
  • 数据结构基础--散列表
  • 【Redis】-- 主从复制
  • 输入1.8V~5.5V 输出28V DCDC升压芯片TLV61046A
  • Windows 上安装 FFmpeg 8.0(2025 版)——从“手动解压”到“一条命令”的进化之路
  • 红黑树(RBTree)知识总结
  • 若依框架前端通过 nginx docker 镜像本地运行
  • 二十、瑞萨RZT2N2 PROFINET SDK正式发布
  • SpringAI框架接入Deepseek和豆包实现智能聊天
  • 江协科技STM32课程笔记(一) —GPIO
  • 江协科技STM32课程笔记(二)—外部中断EXTI
  • 科技信息差(9.15)
  • 龙珠KS6 10.5T矿机评测:性能、功耗、噪音与冷却分析
  • 打工人日报#20250915
  • 新一代车载诊断框架简介
  • 05-索引-性能分析
  • 【数据工程】 2. Unix 基础与文件操作
  • 第四课、 TypeScript 中 Cocos 的生命周期
  • 联邦学习论文分享:DPD-fVAE
  • Pairwise排序损失:让机器学会排序的艺术
  • 硬件开发—IMX6ULL裸机—UART通信
  • 蓝牙上位机开发指南
  • 【课堂笔记】复变函数-1
  • 谈谈人大金仓数据库
  • C#调用钉钉API发送通知教程
  • 电子电气架构 --- 产线EOL为何需要智能升级?