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

[Linux]条件变量:实现线程同步(什么是条件变量、为什么需要条件变量,怎么使用条件变量(接口)、例子,代码演示(生产者消费者模式))

目录

 

一、条件变量

1.什么是条件变量

故事说明

2、为什么需要使用条件变量

竞态条件

 3.什么是同步 

饥饿问题

二、条件变量的接口

1.pthread_cond_t

2.初始化(pthread_cond_init)

3.销毁(pthread_cond_destroy)

4.等待(pthread_cond_wait)

5.唤醒(pthread_cond_signal && pthread_cond_broadcast)

pthread_cond_signal

pthread_cond_broadcast

三、使用演示 (模拟生产者消费者模式)


 

一、条件变量

1.什么是条件变量

条件变量(Condition Variable)是一种用于线程同步的机制,通常与互斥锁(Mutex)一起使用。条件变量提供了一种线程间的通信机制,允许一个线程等待另一个线程满足某个条件后再继续执行。

故事说明

现在小明要在在一张桌子上放一个苹果,而旁边有一群蒙着眼睛的人,因为他们的眼睛被蒙着,他们如果想拿到这个苹果,就会时不时来桌子前摸一摸看看桌子是否有苹果,并且谁来桌子前摸苹果是无序的,这时的场面就很混乱,小明一看不行,于是小明就桌子上放了个铃铛,并且组织需要苹果的人排好队,有苹果小米就会摇响铃铛,排在第一个的人就拿走苹果,排到队尾等待被唤醒。此时混乱的场面就显得尽然有序了。在本故事中,小明就是操作系统,苹果就是临界资源,一群蒙着眼睛都人就是多线程,铃铛就是条件变量,排队就是实现同步,摇响铃铛就是唤醒线程。

2、为什么需要使用条件变量

使用条件变量主要是因为它们提供了在多线程编程中一种有效的同步机制。当多个线程需要等待某个特定条件成立才能继续执行时,条件变量就显得尤为重要。通过条件变量,线程可以安全地进入等待状态,直到被其他线程显式地唤醒或满足等待的条件。这有助于避免线程的无谓轮询或忙等待,提高了系统的响应能力和效率。

注意:在使用条件变量时,必须确保与互斥锁一起使用,以避免竞态条件的发生

竞态条件

竞态条件(Race Condition)是指在设备或系统尝试同时执行两个或多个操作时,由于操作顺序不当而导致的不期望的结果。简单来说就是因为时序问题,而导致程序异常。
 

 3.什么是同步 

在保证数据安全的前提下,让线程能够按照某种特定的顺序访问临界资源,从而有效避免饥饿问题,叫做同步。

饥饿问题

饥饿问题指的是某些线程由于某种原因无法获得它们所需要的资源或执行机会,导致它们长时间得不到处理,甚至永远得不到处理的现象。这种情况通常发生在多个线程竞争有限资源时,其中一些线程可能因为优先级过低、调度算法的不公平性、同步机制使用不当或其他原因而无法获得足够的执行时间。

二、条件变量的接口

1.pthread_cond_t

pthread_cond_t 是 POSIX 线程库(Pthreads)中用于表示条件变量的数据类型。

2.初始化(pthread_cond_init)

功能:初始化条件变量
原型

#include <pthread.h>

方式一(pthread_cond_t是局部全局都可以用):

int pthread_cond_init(pthread_cond_t *restrict cond,

                       const pthread_condattr_t *restrict attr);

方式二(pthread_cond_t是全局变量时):
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

注意:restrict 是一个类型限定符,它用于告知编译器两个指针不会指向同一个内存位置,这样编译器可以生成更高效的代码

参数

  • cond:一个指向 pthread_cond_t 类型的指针,用于存储初始化后的条件变量。
  • attr:一个指向 pthread_condattr_t 类型的指针,用于指定条件变量的属性。通常可以传递 NULL(nullptr),以使用默认属性。

返回值

  • 如果成功,返回 0。
  • 如果失败,返回错误码。

使用例子:

#include <pthread.h>  
#include <stdio.h>  pthread_cond_t cond; // 全局 pthread_cond_t 变量  int main() {  int rc;  // 显式初始化全局 pthread_cond_t 变量  rc = pthread_cond_init(&cond, NULL);  if (rc != 0) {  printf("Cond init failed: %d\n", rc);  return 1;  }  // ... 其他代码,包括线程创建和同步 ...  // 在不再需要条件变量时销毁它  //...return 0;  
}

3.销毁(pthread_cond_destroy)

功能:销毁条件变量
原型

#include <pthread.h>

int pthread_cond_destroy(pthread_cond_t *cond);

参数

  • cond:指向要销毁的条件变量的指针。

返回值

  • 如果成功,返回 0。
  • 如果失败,返回错误码。

4.等待(pthread_cond_wait)

功能:阻塞当前线程,直到指定的条件变量被其他线程信号通知或广播。
原型

#include <pthread.h>

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

参数

  • cond:指向条件变量的指针。
  • mutex:指向互斥锁的指针,该互斥锁应该在调用 pthread_cond_wait 之前由当前线程锁定。

返回值

  • 如果成功,返回 0。
  • 如果失败,返回错误码。

5.唤醒(pthread_cond_signal && pthread_cond_broadcast)

pthread_cond_signal

功能:唤醒正在等待特定条件变量的一个线程
原型

#include <pthread.h>

int pthread_cond_signal(pthread_cond_t *cond);

参数

  • cond:指向要发送信号(广播)的条件变量的指针。

返回值

  • 如果成功,返回 0。
  • 如果失败,返回错误码。

pthread_cond_broadcast

功能:用于唤醒所有正在等待指定条件变量的线程
原型

#include <pthread.h>

int pthread_cond_broadcast(pthread_cond_t *cond);

参数

  • cond:指向要发送信号(广播)的条件变量的指针。

返回值

  • 如果成功,返回 0。
  • 如果失败,返回错误码。

三、使用演示 (模拟生产者消费者模式)

说明:模拟生产者消费者模式

注意:使用pthrad原生线程库(POSIX库)要链接库:-lpthread

不会连接动态库的可以看我这篇文章:[Linux]动静态库(什么是动静态库,怎么生成动静态库,怎么使用(连接)动静态库)-CSDN博客

cond.cc

#include <iostream>
#include <pthread.h>
#include <vector>
#include <string>
#include <unistd.h>
using namespace std;// 定义条件变量和互斥锁
// 全局的初始化方式
pthread_cond_t cond_var = PTHREAD_COND_INITIALIZER;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;// 共享变量,用于线程间的同步
int shared_data = 0;// 线程函数,模拟生产者
void *producer(void *args)
{string producer_name = static_cast<char *>(args);// 生产数据,并通知消费者while (1){// 锁定互斥锁pthread_mutex_lock(&mutex);// 生产数据(这里只是简单地递增shared_data)shared_data++;cout << "I is " << producer_name << " "<< " Producer produced data: "<< shared_data << endl;// 唤醒等待的消费者线程pthread_cond_signal(&cond_var);// 解锁互斥锁pthread_mutex_unlock(&mutex);// 模拟生产耗时sleep(1);}return nullptr;
}// 线程函数,模拟消费者
void *consumer(void *args)
{string consumer_name = static_cast<char *>(args);// 消费数据while (1){// 锁定互斥锁pthread_mutex_lock(&mutex);// 等待生产者生产数据while (shared_data == 0){// 等待条件变量,解锁互斥锁,进入等待状态pthread_cond_wait(&cond_var, &mutex);}// 消费数据(这里只是简单地递减shared_data)shared_data--;cout << "I is " << consumer_name << " "<< " Consumer consumed data: "<< shared_data << endl;cout << "-----------------------------------"<< endl;// 解锁互斥锁pthread_mutex_unlock(&mutex);// 模拟消费耗时sleep(4);}return nullptr;
}int main()
{int producer_thread_num = 5; // 生产者人数int consumer_thread_num = 10; // 消费者人数vector<pthread_t> producers;vector<pthread_t> consumers;for (int i = 0; i < producer_thread_num; i++){pthread_t producer_thread; // 生产者char buffer[64];sprintf(buffer, "producer-%d", i + 1);// 创建生产者线程if (pthread_create(&producer_thread, nullptr, producer, buffer) != 0){perror("pthread_create producer");exit(EXIT_FAILURE);}producers.push_back(producer_thread);//保存pthread_t,以备等待回收}for (int i = 0; i < consumer_thread_num; i++){pthread_t consumer_thread; // 消费者// 创建消费者线程char buffer[64];sprintf(buffer, "consumer-%d", i + 1);if (pthread_create(&consumer_thread, nullptr, consumer, buffer) != 0){perror("pthread_create consumer");exit(EXIT_FAILURE);}consumers.push_back(consumer_thread);//保存pthread_t,以备等待回收}// 等待线程结束for (auto& thraed:producers){   pthread_join(thraed, nullptr);}for (auto& thraed:consumers){   pthread_join(thraed, nullptr);}// 销毁条件变量pthread_cond_destroy(&cond_var);// 销毁锁pthread_mutex_destroy(&mutex);return 0;
}

Makefile

mycond:cond.ccg++ -o $@ $^ -std=c++11 -lpthread
PHONY:clean
clean:rm -f mycond

结果

0d98aa1785264b589c7f43f857e454a2.png

 

相关文章:

[Linux]条件变量:实现线程同步(什么是条件变量、为什么需要条件变量,怎么使用条件变量(接口)、例子,代码演示(生产者消费者模式))

目录 一、条件变量 1.什么是条件变量 故事说明 2、为什么需要使用条件变量 竞态条件 3.什么是同步 饥饿问题 二、条件变量的接口 1.pthread_cond_t 2.初始化&#xff08;pthread_cond_init&#xff09; 3.销毁&#xff08;pthread_cond_destroy&#xff09; 4.等待&…...

从Java到json:探索 Jackson 的魔力

引言 Jackson简介 Jackson是一个用于处理JSON数据的开源Java库。JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,易于阅读和编写,同时也易于计算机解析和生成。在Java领域,Jackson已经成为处理JSON数据的事实标准库。它提供了丰富的功能,包括将Java对象转…...

Docker之docker compose!!!!

一、概述 是 Docker 官方提供的一款开源工具&#xff0c;主要用于简化在单个主机上定义和运行多容器 Docker 应用的过程。它的核心作用是容器编排&#xff0c;使得开发者能够在一个统一的环境中以声明式的方式管理多容器应用的服务及其依赖关系。 也就是说Docker Compose是一个…...

shardingsphere+达梦+jpa项目改造适配中遇到的一些问题与解决

问题一&#xff1a;shardingsphere.dialect.exception.syntax.database.UnknownDatabaseException 解决&#xff1a; jdbcTemplate 类注入有问题&#xff0c;如&#xff1a; 1&#xff09;如果使用Resource注解引入该类时&#xff0c;变量名需要与初始化时Bean修饰的方法名相…...

YOLOV9训练自己的数据集

1.代码下载地址GitHub - WongKinYiu/yolov9: Implementation of paper - YOLOv9: Learning What You Want to Learn Using Programmable Gradient Information 2.准备自己的数据集 这里数据集我以SAR数据集为例 具体的下载链接如下所示&#xff1a; 链接&#xff1a;https:/…...

UG NX二次开发(C++)-CAM-获取加工操作的四种方法

文章目录 1、前言2、采用选中工序导航器获取操作的Tag_t3、采用遍历对象的方法获取操作的Tag_t4、采用Collection遍历获取操作对象NXOpen::CAM::Operation5、采用FindObject获取操作对象NXOpen::CAM::Operation6、以上4种方法封装成类 Class CAMOperation6.1 CAMOperation.h文件…...

python共享单车信息系统的设计与实现flask-django-php-nodejs

课题主要分为二大模块&#xff1a;即管理员模块和用户模块&#xff0c;主要功能包括&#xff1a;用户、区域、共享单车、单车租赁、租赁归还、报修信息、检修信息等&#xff1b; 语言&#xff1a;Python 框架&#xff1a;django/flask 软件版本&#xff1a;python3.7.7 数据库…...

Python之Web开发中级教程----Django站点管理

Python之Web开发中级教程----Django站点管理 网站的开发分为两部分&#xff1a;内容发布和公共访问 内容发布是由网站的管理员负责查看、添加、修改、删除数据 Django能够根据定义的模型类自动地生成管理模块 使用Django的管理模块, 需要按照如下步骤操作 : 1.管理界面本地…...

Spring Boot项目中使用MyBatis连接达梦数据库6

在开发中&#xff0c;使用Spring Boot框架结合MyBatis来操作数据库是一种常见的做法。本篇博客将介绍如何在Spring Boot项目中配置MyBatis来连接达梦数据库6&#xff0c;并提供一个简单的示例供参考。(达梦六不仅分表还分模式.) 我拿SYSTEM表的LPS模式下面Student表做案例。 1.…...

Matlab快捷键与函数

注释&#xff1a;注释对于代码的重要性我们就不做过多的解释了。不做注释的代码不是好代码。选中要注释的语句&#xff0c;按快捷键CtrlR,或者在命令行窗口上面的注释地方可以进行注释。当然也可以直接在语句前面“%”就可以&#xff08;注意&#xff1a;一定要用英文符号&…...

接雨水-热题 100?-Lua 中文代码解题第4题

接雨水-热题 100&#xff1f;-Lua 中文代码解题第4题 给定 n 个非负整数表示每个宽度为 1 的柱子的高度图&#xff0c;计算按此排列的柱子&#xff0c;下雨之后能接多少雨水。 示例 1&#xff1a; 输入&#xff1a;height [0,1,0,2,1,0,1,3,2,1,2,1] 输出&#xff1a;6 解释…...

JVM内存溢出排查

JVM内存溢出排查主要涉及到定位问题发生的原因以及确定哪些对象占用了过多的内存。以下是一些排查内存溢出的基本步骤&#xff1a; 查看异常信息&#xff1a; 当JVM发生内存溢出时&#xff0c;会抛出OutOfMemoryError异常&#xff0c;并伴随异常信息。这些信息可以帮助初步定位…...

Leetcode 200. 岛屿数量

心路历程&#xff1a; 在没有看图论这一章之前看这道题没什么直接的思路&#xff0c;在看完图论之后&#xff0c;学着使用DFS和BFS去套用解决。第一次自己做的时候还是遇到了很多小问题。整体思路很流畅&#xff0c;但是需要处理的细节第一次没怎么处理好&#xff0c;花了很多…...

多线程基础 -概念、创建、等待、分离、终止

文章目录 一、 线程概念1. 什么是线程2. 线程的优点3.线程的缺点4. 线程异常5. 线程用途 二、 Linux进程VS线程1. 进程和线程2. 进程和线程的地址空间3. 进程和线程的关系 三、Linux线程控制1. POSIX线程库2. 线程创建3. 线程ID及进程地址空间布局4. 线程终止5. 线程等待6. 线程…...

【Vue3】走进Pinia,学习Pinia,使用Pinia

&#x1f497;&#x1f497;&#x1f497;欢迎来到我的博客&#xff0c;你将找到有关如何使用技术解决问题的文章&#xff0c;也会找到某个技术的学习路线。无论你是何种职业&#xff0c;我都希望我的博客对你有所帮助。最后不要忘记订阅我的博客以获取最新文章&#xff0c;也欢…...

【TB作品】430单片机,单片机串口多功能通信,Proteus仿真

文章目录 题目功能仿真图程序介绍代码、仿真、原理图、PCB 题目 60、单片机串口多功能通信 基本要求: 设计一串口通信程序,波特率38400,通过RS232与PC机通信。 自动循环发送数据串(设计在程序中) 接收并存储和显示该数据串 在发送端定义10个ASCII码键0-9 按键发送单字节,PC机接…...

【C++ leetcode】双指针问题

1. 611. 有效三角形的个数 题目 给定一个包含非负整数的数组 nums &#xff0c;返回其中可以组成三角形三条边的三元组个数。 题目链接 . - 力扣&#xff08;LeetCode&#xff09; 画图 和 文字 分析 判断是否是三角形要得到三边&#xff0c;由于遍历三边要套三层循环&#x…...

Kubernetes集群部署

1.集群环境搭建 1.1 环境规划 kubernetes集群大体上分为两类&#xff1a;一主多从和多主多从。 一主多从&#xff1a;一台Master节点和多台Node节点&#xff0c;搭建简单&#xff0c;但是有单机故障风险&#xff0c;适合用于测试环境多主多从&#xff1a;多台Master节点和多…...

深拷贝与浅拷贝

深拷贝与浅拷贝是在进行对象复制时常见的两种方式&#xff0c;这两个概念其实比较混淆&#xff0c;面试中也经常出现&#xff0c;但是实际开发很少用到&#xff0c;所以本文就来详细讲解一下&#xff0c;让大家不再迷惑。 浅拷贝只是复制了对象的引用&#xff08;地址&#xf…...

golang学习网址

.1LearnKu 终身编程者的知识社区 https://learnku.com/...

深度学习在微纳光子学中的应用

深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向&#xff1a; 逆向设计 通过神经网络快速预测微纳结构的光学响应&#xff0c;替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…...

eNSP-Cloud(实现本地电脑与eNSP内设备之间通信)

说明&#xff1a; 想象一下&#xff0c;你正在用eNSP搭建一个虚拟的网络世界&#xff0c;里面有虚拟的路由器、交换机、电脑&#xff08;PC&#xff09;等等。这些设备都在你的电脑里面“运行”&#xff0c;它们之间可以互相通信&#xff0c;就像一个封闭的小王国。 但是&#…...

C++初阶-list的底层

目录 1.std::list实现的所有代码 2.list的简单介绍 2.1实现list的类 2.2_list_iterator的实现 2.2.1_list_iterator实现的原因和好处 2.2.2_list_iterator实现 2.3_list_node的实现 2.3.1. 避免递归的模板依赖 2.3.2. 内存布局一致性 2.3.3. 类型安全的替代方案 2.3.…...

在鸿蒙HarmonyOS 5中实现抖音风格的点赞功能

下面我将详细介绍如何使用HarmonyOS SDK在HarmonyOS 5中实现类似抖音的点赞功能&#xff0c;包括动画效果、数据同步和交互优化。 1. 基础点赞功能实现 1.1 创建数据模型 // VideoModel.ets export class VideoModel {id: string "";title: string ""…...

Java如何权衡是使用无序的数组还是有序的数组

在 Java 中,选择有序数组还是无序数组取决于具体场景的性能需求与操作特点。以下是关键权衡因素及决策指南: ⚖️ 核心权衡维度 维度有序数组无序数组查询性能二分查找 O(log n) ✅线性扫描 O(n) ❌插入/删除需移位维护顺序 O(n) ❌直接操作尾部 O(1) ✅内存开销与无序数组相…...

WordPress插件:AI多语言写作与智能配图、免费AI模型、SEO文章生成

厌倦手动写WordPress文章&#xff1f;AI自动生成&#xff0c;效率提升10倍&#xff01; 支持多语言、自动配图、定时发布&#xff0c;让内容创作更轻松&#xff01; AI内容生成 → 不想每天写文章&#xff1f;AI一键生成高质量内容&#xff01;多语言支持 → 跨境电商必备&am…...

c#开发AI模型对话

AI模型 前面已经介绍了一般AI模型本地部署&#xff0c;直接调用现成的模型数据。这里主要讲述讲接口集成到我们自己的程序中使用方式。 微软提供了ML.NET来开发和使用AI模型&#xff0c;但是目前国内可能使用不多&#xff0c;至少实践例子很少看见。开发训练模型就不介绍了&am…...

如何在网页里填写 PDF 表格?

有时候&#xff0c;你可能希望用户能在你的网站上填写 PDF 表单。然而&#xff0c;这件事并不简单&#xff0c;因为 PDF 并不是一种原生的网页格式。虽然浏览器可以显示 PDF 文件&#xff0c;但原生并不支持编辑或填写它们。更糟的是&#xff0c;如果你想收集表单数据&#xff…...

SiFli 52把Imagie图片,Font字体资源放在指定位置,编译成指定img.bin和font.bin的问题

分区配置 (ptab.json) img 属性介绍&#xff1a; img 属性指定分区存放的 image 名称&#xff0c;指定的 image 名称必须是当前工程生成的 binary 。 如果 binary 有多个文件&#xff0c;则以 proj_name:binary_name 格式指定文件名&#xff0c; proj_name 为工程 名&…...

深度学习水论文:mamba+图像增强

&#x1f9c0;当前视觉领域对高效长序列建模需求激增&#xff0c;对Mamba图像增强这方向的研究自然也逐渐火热。原因在于其高效长程建模&#xff0c;以及动态计算优势&#xff0c;在图像质量提升和细节恢复方面有难以替代的作用。 &#x1f9c0;因此短时间内&#xff0c;就有不…...