【Linux】线程同步

【Linux】线程同步

📝前言:

上篇文章我们讲解了【Linux】线程互斥,这篇文章我们来讲讲Linux——线程同步

🎬个人简介:努力学习ing
📋个人专栏:Linux
🎀ZEEKLOG主页 愚润求学
🌄其他专栏:C++学习笔记C语言入门基础python入门基础C++刷题专栏

目录

一,同步定义

在保证数据安全的前提下,让线程能够按照某种特定的顺序访问临界资源。我们可以回忆一下管道,管道的读写行为是具有顺序性的,这就是一种同步。

为什么需要同步?
当两个线程(一个读端,一个写端)同时访问一块内存的时候,为了保证线程安全性,需要加锁。但是,如果此时这块内存写端还没有写入内容,读端一直占据着锁(频繁申请,且每次都竞争到锁),写端就无法写入内容,读端也无法读到内容。

为此,我们可以让读端没读到内容的时候就进入等待(把锁给释放),然后唤醒写端(让写端拿锁)

如果有多个读端,又一个读端“离锁最近”一直在读,其他读端读不到怎么办?

这时候我们可以设计一个“等待队列”,让每个读端申请完一次锁以后,就要重新进入队列的末尾开始重新排队。

条件变量就具有这样的唤醒 + 等待队列的功能

二,条件变量

1. 基本介绍

解决线程间因共享资源状态变化而需要互相通知的问题,且保证公平性。

作用

  • 让线程等待某个条件满足(如共享资源可用、数据准备完成等)。
  • 当条件满足时,通知等待的线程继续执行。

关键操作

  • 等待(Wait):线程阻塞自己,放入指定条件变量的等待队列,等待条件变量被唤醒。
  • 通知(Signal/Broadcast):其他线程在条件满足时唤醒等待的线程。

与互斥锁的配合

  • 条件变量本身不具备锁的功能,需配合互斥锁使用,确保对共享资源状态的检查和修改是原子操作。

2. 接口

基本上和mutex的接口一样。

2.1 初始化和销毁

初始化

静态分配

pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
  • pthread_cond_t:条件变量cond的类型

动态分配

intpthread_cond_init(pthread_cond_t *restrict cond,const pthread_condattr_t *restrict attr);
  • cond:要初始化的条件变量
  • attr:nullptr
销毁
intpthread_cond_destroy(pthread_cond_t *cond)

2.2 等待和通知

等待

cond_wait包含两步操作:解锁 + 等待(类比--是3条汇编)

为什么条件变量要配合锁使用?

如果解锁 + 等待是两个动作,不是原子的,则会出现以下问题:

  1. 线程 A 条件不满足 → 解锁
  2. 线程 A 被切换,线程 B 唤醒线程 A(但是线程 A 还未进入等待,收不到这个唤醒,唤醒被错过)
  3. 切回线程 A ,A 进入等待,但接受不到唤醒信号了,变成死锁

常用接口
无限期阻塞等待
(只有唤醒才醒来)

intpthread_cond_wait( pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);
  • cond:在哪个条件变量上等待
  • mutex:对应的互斥锁
  • 在进入等待的时候会释放锁,等待被唤醒后要重新申请锁
    • 申请成功,就会接着运行后面的语句
    • 申请不成功,就会在“锁上”阻塞(直到拿到锁)
  • 被唤醒就是在临界区被唤醒,然后继续往后执行

时间片等待(超时了就醒来)

intpthread_cond_timedwait( pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex,conststructtimespec*restrict abstime );
  • 绝对超时时间(如果到这个时间还没有被唤醒,就醒来)
通知(唤醒)

全部唤醒

intpthread_cond_broadcast(pthread_cond_t *cond);

如果cond里面没有能唤醒的,则什么都不做。

只唤醒条件变量的队头

intpthread_cond_signal(pthread_cond_t *cond);

三,使用经典规范

1. 等待代码

pthread_mutex_lock(&mutex);// 访问临界区先加锁while(条件为假) // 判断条件是否满足,实现同步pthread_cond_wait(cond, mutex);// 条件不满足:释放锁 + 等待 访问临界资源;// 修改共享资源(条件可能再次变为不满足)pthread_mutex_unlock(&mutex);// 访问完临界区解锁

为什么用 while 而不是 if
虚假唤醒:

  • pthread_cond_wait执行错误(没有进入等待),然后返回
  • pthread_cond_wait一下被唤醒多个线程,但是一个线程执行完以后,条件再次变为不满足。此时其他线程过了if判断且被唤醒,再次向下执行,就会出现错误。所以要循环重新检查条件

2. 唤醒代码

在另一个线程内:

pthread_mutex_lock(&mutex);// 代表也是在加锁的临界区内 访问临界资源(条件);// 使条件为真pthread_cond_signal(&cond);// (当条件为真,且有可以唤醒的)唤醒一个等待的线程pthread_mutex_unlock(&mutex);

四,生产者消费者模型

1. 基本介绍

生产者消费者模式就是通过⼀个容器(交易场所)来解决生产者和消费者的强耦合问题。生产者往交易场所生产数据,消费者从交易场所获取数据,生产者的生产不需要等待消费者处理完数据。从而实现生产和消费的并发运行。

在这里插入图片描述

生产者与消费者关系:

  • 生产者和生产者:互斥
  • 消费者与消费者:互斥
  • 生产者和消费者:互斥 + 同步

特点:

  • 3 个要素:生产者,消费者,一个交易场所(临界资源)
  • 3 个关系:(上面提到的生产者与消费者的关系)
  • 2 种角色:生产者和消费者(分别由线程承担)
  • 1 个交易场所:以特定结构构成的一种“内存”(临界资源)

优势

  • 支持生产和消费解耦
  • 忙闲不均
  • 提高效率

2. 使用示例

我们实现一个基于BlockingQueue的生产者消费者模型
BlockingQueue核心要点:

  • Pop 的时候,如果队列里面没东西,则阻塞等待生产者Push
  • Push 的时候,如果队列满了,则阻塞等待消费者Pop
// BlockQueue.hpp#include<pthread.h>#include<iostream>#include<string>#include<queue>template<typenameT>classBlockQueue{private:boolEmpty(){return blockqueue.size()==0;}boolFull(){return blockqueue.size()== _cap;}public:BlockQueue(int cap):_prod_wait_num(0),_coms_wait_num(0),_cap(cap){pthread_mutex_init(&_mutex,nullptr);pthread_cond_init(&_full_cond,nullptr);pthread_cond_init(&_empty_cond,nullptr);}~BlockQueue(){pthread_mutex_destroy(&_mutex);pthread_cond_destroy(&_full_cond);pthread_cond_destroy(&_empty_cond);}voidPush(const T &task){pthread_mutex_lock(&_mutex);while(Full()){ _prod_wait_num++;pthread_cond_wait(&_full_cond,&_mutex); _prod_wait_num--;} blockqueue.push(task);// 修改临界资源 std::cout <<"生产者: "<<pthread_self()<<" 生产了一个任务"<< std::endl;if(_coms_wait_num >0)pthread_cond_signal(&_empty_cond);pthread_mutex_unlock(&_mutex);} T Pop(){pthread_mutex_lock(&_mutex);// 等待while(Empty()){ _coms_wait_num++;// 如果唤醒,就在临界区被唤醒,然后继续往下执行pthread_cond_wait(&_empty_cond,&_mutex); _coms_wait_num--;} T task = blockqueue.front(); blockqueue.pop(); std::cout <<"消费者: "<<pthread_self()<<" 处理了一个任务"<< std::endl;// 唤醒if(_prod_wait_num >0)pthread_cond_signal(&_full_cond);pthread_mutex_unlock(&_mutex);return task;}private: std::queue<T> blockqueue; pthread_mutex_t _mutex; pthread_cond_t _full_cond; pthread_cond_t _empty_cond;int _cap;// 设置容量上限int _prod_wait_num;int _coms_wait_num;};// Main.cpp#include"BlockQueue.hpp"#include<vector>#include<unistd.h>#include<atomic>#include"Task.hpp"void*Prod(void* args){ BlockQueue<task_t>* ptr =static_cast<BlockQueue<task_t>*>(args);while(true){// sleep(1); ptr->Push(Download);}returnnullptr;}void*Coms(void* args){ BlockQueue<task_t>* ptr =static_cast<BlockQueue<task_t>*>(args);while(true){ task_t task = ptr->Pop();// 获取任务// cout 输出也会有并发问题,暂时放在Pop里面task();}returnnullptr;}intmain(){ BlockQueue<int>super(3);// 单生产者,单消费者 pthread_t p[1], c[1];pthread_create(&p[0],nullptr, Prod,&super);// 生成者pthread_create(&c[0],nullptr, Coms,&super);// 消费者pthread_join(p[0],nullptr);pthread_join(c[0],nullptr);// // 多生产者,多消费者// pthread_t p[2], c[2];// pthread_create(&p[0], nullptr, Prod, &super); // 生成者// pthread_create(&c[0], nullptr, Coms, &super); // 消费者// pthread_create(&p[1], nullptr, Prod, &super); // 生成者// pthread_create(&c[1], nullptr, Coms, &super); // 消费者// pthread_join(p[0], nullptr);// pthread_join(c[0], nullptr);// pthread_join(p[1], nullptr);// pthread_join(c[1], nullptr);return0;}

当然,我的代码还有很多未处理的BUG,仅供参考。

运行结果(单生产者和单消费者):
生成者快,消费者慢:先生产一堆,然后消费一个生产一个。

在这里插入图片描述


生产者满,消费者慢,生产一个消费一个。

在这里插入图片描述

🌈我的分享也就到此结束啦🌈
要是我的分享也能对你的学习起到帮助,那简直是太酷啦!
若有不足,还请大家多多指正,我们一起学习交流!
📢公主,王子:点赞👍→收藏⭐→关注🔍
感谢大家的观看和支持!祝大家都能得偿所愿,天天开心!!!

Read more

苹果最贵手机要来了!折叠屏iPhone将于9月亮相;部分高校严禁校内使用OpenClaw;黄仁勋预言:传统软件和APP或将消失 | 极客头条

苹果最贵手机要来了!折叠屏iPhone将于9月亮相;部分高校严禁校内使用OpenClaw;黄仁勋预言:传统软件和APP或将消失 | 极客头条

「极客头条」—— 技术人员的新闻圈! ZEEKLOG 的读者朋友们好,「极客头条」来啦,快来看今天都有哪些值得我们技术人关注的重要新闻吧。(投稿或寻求报道:[email protected]) 整理 | 郑丽媛 出品 | ZEEKLOG(ID:ZEEKLOGnews) 一分钟速览新闻点! * 多所高校要求警惕 OpenClaw 安全风险,部分严禁校内使用 * 荣耀 CEO 李健:荣耀机器人全栈自研,将聚焦消费市场 * 马化腾凌晨 2 点发声:还有一批龙虾系产品陆续赶来 * 前快手语言大模型中心负责人张富峥,已加入智源人工智能研究院,负责 LLM 方向 * 最新全球 AI 应用百强榜发布,豆包/DeepSeek/千问上榜 * 苹果折叠 iPhone 将于九月亮相,融合 iPhone 与 iPad 体验

By Ne0inhk
不止“996”!曝硅谷AI创业圈「极限工作制」:每天16小时、凌晨3点下班、周末也在写代码

不止“996”!曝硅谷AI创业圈「极限工作制」:每天16小时、凌晨3点下班、周末也在写代码

编译 | 郑丽媛 出品 | ZEEKLOG(ID:ZEEKLOGnews) “如果你周日去旧金山的咖啡馆,会发现几乎每个人都在工作。” 这是 AI 创业公司 Mythril 联合创始人 Sanju Lokuhitige 最近最直观的感受。去年 11 月,他特地搬到旧金山,只为了更接近 AI 创业浪潮的中心。但很快,他也被卷入了这股浪潮带来的另一面——一种越来越极端的工作文化。 Lokuhitige 坦言,他现在几乎每天工作 12 小时,每周 7 天。除了每周少数几场刻意安排的社交活动(主要是为了和创业者们建立联系),其余时间几乎都在写代码、做产品。 “有时候我整整一天都在编程,”他说,“我基本没有什么工作与生活的平衡。”而这样的生活,在如今的 AI 创业圈里并不算罕见。 旧金山 AI 创业圈的真实日常 一位在旧金山一家 AI

By Ne0inhk
黄仁勋公开发文:传统软件开发模式终结,参与AI不必非得拥有计算机博士学位

黄仁勋公开发文:传统软件开发模式终结,参与AI不必非得拥有计算机博士学位

AI 究竟是什么?在 NVIDIA CEO 黄仁勋看来,它早已不只是聊天机器人或某个大模型,而是一种正在迅速成形的“新型基础设施”。 近日,黄仁勋在英伟达官网发布了一篇长文,提出一个颇具形象的比喻——AI 就像一块“五层蛋糕”。从最底层的能源,到芯片、基础设施、模型,再到最上层的应用,人工智能正在形成一整套完整的产业技术栈,并像电力和互联网一样,逐渐成为现代社会的底层能力。 这也是黄仁勋自 2016 年以来公开发表的第七篇长文。在这篇文章中,他从计算机发展史与第一性原理出发,试图解释 AI 技术栈为何会演化成如今的形态,以及为什么全球正在掀起一场规模空前的 AI 基础设施建设。 在他看来,过去几十年的软件大多是预先编写好的程序:人类设计好算法,计算机按指令执行,数据被结构化存储在数据库中,通过精确查询调用。而 AI 的出现打破了这一模式——计算机开始能够理解图像、文本和声音,并根据上下文实时生成答案、推理结果甚至新的内容。 正因为智能不再是预先写好的代码,而是实时生成的能力,支撑它运行的整个计算体系也必须被重新设计。

By Ne0inhk
猛裁1.6万人后,网站再崩6小时、一周4次重大事故!官方“紧急复盘”:跟裁员无关,也不是AI写代码的锅

猛裁1.6万人后,网站再崩6小时、一周4次重大事故!官方“紧急复盘”:跟裁员无关,也不是AI写代码的锅

整理 | 郑丽媛 出品 | ZEEKLOG(ID:ZEEKLOGnews) 过去几年里,科技公司几乎都在同一件事上加速:让 AI 参与写代码。 从自动补全、自动生成函数,到直接修改系统配置,生成式 AI 已经逐渐走进真实生产环境。但最近发生在亚马逊的一连串事故,却给整个行业泼了一盆冷水——当 AI 开始真正参与生产环境开发时,事情可能远比想象复杂。 最近,多家媒体披露,本周二亚马逊内部紧急召开了一场工程“深度复盘(deep dive)”会议,专门讨论最近频繁出现的系统故障——其中,一个被反复提及的关键词是:AI 辅助代码。 一周 4 次严重事故,亚马逊内部紧急复盘 事情的起点,是最近一段时间亚马逊系统稳定性明显下降。 负责亚马逊网站技术架构的高级副总裁 Dave Treadwell 在一封内部邮件中坦言:“各位,正如大家可能已经知道的,最近网站及相关基础设施的可用性确实不太理想。” 为此,公司决定把原本每周例行举行的技术会议

By Ne0inhk