[linux仓库]多线程同步:基于POSIX信号量实现生产者-消费者模型[线程·柒]

[linux仓库]多线程同步:基于POSIX信号量实现生产者-消费者模型[线程·柒]


🌟 各位看官好,我是egoist2023

🌍 Linux == Linux is not Unix !


🚀 今天来学习Linux的System V信号量,基于该信号量实现生产者消费者模型的代码。

👍 如果觉得这篇文章有帮助,欢迎您一键三连,分享更多人哦!

目录

回顾System V信号量

认识信号量接口

环形队列

单单CP场景

代码实现

生产数据

消费数据

多多CP场景

总结


回顾System V信号量

还记得这张图不?这是当年在讲进程通信时候,通过讲述电影院的故事,画出的图.上一节我们又有了阻塞队列的知识储备.

阻塞队列当成整体使用,如果此时拆分成一个个小资源呢?让不同的线程访问同一块资源的不同部分,那么不就相当于允许多个线程并发访问同一块资源了吗

整体使用就是要有互斥能力;

局部使用,访问错了,分多资源应该要规避
  •  放过多线程进入,本质就是访问信号量,而信号量本质是一把计数器!(信号量就是一个计数器,可以理解为锁+整数) --> 那么该如何表示信号量还剩多少资源,资源被申请多少了呢? PV操作

要申请信号量 --> 前提是看到同一个信号量 --> 信号量本身就要是共享资源 --> 如何保证自己的安全? --> PV操作是原子性的

  •  让不同的线程,访问同一块资源的不同部分,这个资源在哪里呢?需要让程序员做!

POSIX信号量

POSIX信号量和SystemV信号量作⽤相同,都是⽤于同步操作,达到⽆冲突的访问共享资源⽬的。但POSIX可以⽤于线程间同步。

认识信号量接口

int sem_init(sem_t *sem, int pshared, unsigned int value);

功能:初始化信号量

参数:

        pshared:0表⽰线程间共享,⾮零表⽰进程间共享

        value:信号量初始值

int sem_destroy(sem_t *sem);

功能:销毁信号量


int sem_wait(sem_t *sem); P操作

功能:等待信号量,会将信号量的值减1

int sem_post(sem_t *sem); V操作

功能:发布信号量,表⽰资源使⽤完毕,可以归还资源了。将信号量值加1。

上⼀节⽣产者-消费者的例⼦是基于queue的,其空间可以动态分配,现在基于固定⼤⼩的环形队列重写这个程序(POSIX信号量):

环形队列

单单CP场景

我们现在有信号量这个计数器,就很简单的进⾏多线程间的同步过程。

  • 为空的时候,必须让生产者先运行 --> 生产和消费会访问同一个位置 --> 互斥放入数据 --> 这不就是生产和消费者的互斥与同步关系!
  • 为满的时候,必须让消费者先运行 --> 生产和消费又指向同一个位置了! --> 互斥的获取数据      --> 这不就是生产和消费者的互斥与同步关系!
  • 不为空 && 不为满:此时首 != 尾 ,访问的一定不是同一个位置! --> 此时,不就可以并发执行了?!

代码实现

信号量Sem.hpp封装

class Sem { public: Sem(int initnum) : _initnum(initnum) { sem_init(&_sem, 0, _initnum); } void P() { int n = sem_wait(&_sem); (void)n; } void V() { int n = sem_post(&_sem); (void)n; } ~Sem() { sem_destroy(&_sem); } private: sem_t _sem; int _initnum; };

RingQueue.hpp

static int gcap = 5; // for debug template <typename T> class RingQueue { public: RingQueue(int cap = gcap) : _cap(cap), _ring_queue(cap), _space_sem(cap), _data_sem(0), _p_step(0), _c_step(0) { } private: std::vector<T> _ring_queue; // 临界资源 int _cap; Sem _space_sem; Sem _data_sem; // 生产和消费的位置 int _p_step; int _c_step; };
生产数据

因为是单生产单消费:

  1. 不担心同时有两个生产者来访问;
  2. 生产者访问期间,消费者不可能来打扰我,一定不为满,最多也就是不为空&不为满,二者可以并发执行,消费者并不影响生产者
 void Enqueue(const T &in) { _space_sem.P(); { // 生产数据了!有空间,在哪里啊?? _ring_queue[_p_step++] = in; // 维持环形特点 _p_step %= _cap; } _data_sem.V(); }
消费数据

只要消费者走到了*out = _ring_queue[_c_step++,证明队列一定不为空.

 void Pop(T *out) { _data_sem.P(); { *out = _ring_queue[_c_step++]; _c_step %= _cap; } _space_sem.V(); }

多多CP场景

如果是多生产多消费呢?就还需要维护生产者之间、消费者之间的互斥关系,那要加几把锁呢?一把锁就放弃了让生产者和消费者并发运行的情况,降低效率.引入两把锁,生产者之间竞争这把锁,消费者之间竞争者这把锁,本质还是归宿到单生产单消费了

static int gcap = 5; // for debug template <typename T> class RingQueue { public: RingQueue(int cap = gcap) : _cap(cap), _ring_queue(cap), _space_sem(cap), _data_sem(0), _p_step(0), _c_step(0) { } void Pop(T *out) { _data_sem.P(); { LockGuard lockguard(&_c_lock); *out = _ring_queue[_c_step++]; _c_step %= _cap; } _space_sem.V(); } void Enqueue(const T &in) { _space_sem.P(); { LockGuard lockguard(&_p_lock); // 生产数据了!有空间,在哪里啊?? _ring_queue[_p_step++] = in; // 维持环形特点 _p_step %= _cap; } _data_sem.V(); } ~RingQueue() { } private: std::vector<T> _ring_queue; // 临界资源 int _cap; Sem _space_sem; Sem _data_sem; // 生产和消费的位置 int _p_step; int _c_step; // 两把锁 Mutex _p_lock; Mutex _c_lock; };

main.cc

// 基于信号量形成的生产者消费者模型 void *consumer(void *args) { RingQueue<int> *rq = static_cast<RingQueue<int> *>(args); while (true) { int data = 0; rq->Pop(&data); std::cout << "消费了一个数据: " << data << std::endl; } } void *productor(void *args) { RingQueue<int> *rq = static_cast<RingQueue<int> *>(args); int data = 1; while (true) { sleep(1); rq->Enqueue(data); std::cout << "生产了一个数据: " << data << std::endl; data++; } } int main() { RingQueue<int> *rq = new RingQueue<int>(); pthread_t c[2], p[3]; pthread_create(c, nullptr, consumer, (void *)rq); pthread_create(c + 1, nullptr, consumer, (void *)rq); pthread_create(p, nullptr, productor, (void *)rq); pthread_create(p + 1, nullptr, productor, (void *)rq); pthread_create(p + 3, nullptr, productor, (void *)rq); pthread_join(c[0], nullptr); pthread_join(c[1], nullptr); pthread_join(p[0], nullptr); pthread_join(p[1], nullptr); pthread_join(p[2], nullptr); delete rq; return 0; }

总结

本文介绍了Linux系统中信号量的概念及其在多线程编程中的应用。首先回顾了SystemV信号量的工作原理,通过计数器机制实现资源共享。然后详细讲解了POSIX信号量接口(sem_init、sem_wait等)及其在线程同步中的使用方法。文章重点演示了如何基于环形队列实现生产者-消费者模型,包括单生产单消费和多生产多消费场景的实现方案,并提供了完整的代码示例。最后,针对多线程环境提出了使用两把锁的解决方案,以平衡并发性能与线程安全性。

Read more

AI革命先锋:DeepSeek与蓝耘通义万相2.1的无缝融合引领行业智能化变革

AI革命先锋:DeepSeek与蓝耘通义万相2.1的无缝融合引领行业智能化变革

云边有个稻草人-ZEEKLOG博客 目录 引言 一、什么是DeepSeek? 1.1 DeepSeek平台概述 1.2 DeepSeek的核心功能与技术 二、蓝耘通义万相2.1概述 2.1 蓝耘科技简介 2.2 蓝耘通义万相2.1的功能与优势 1. 全链条智能化解决方案 2. 强大的数据处理能力 3. 高效的模型训练与优化 4. 自动化推理与部署 5. 行业专用解决方案 三、蓝耘通义万相2.1与DeepSeek的对比分析 3.1 核心区别 3.2 结合使用的优势 四、蓝耘注册流程 五、DeepSeek与蓝耘通义万相2.1的集成应用 5.1 集成应用场景 1. 智能医疗诊断

By Ne0inhk
基于腾讯云HAI + DeepSeek快速设计自己的个人网页

基于腾讯云HAI + DeepSeek快速设计自己的个人网页

前言:通过结合腾讯云HAI 强大的云端运算能力与DeepSeek先进的 AI技术,本文介绍高效、便捷且低成本的设计一个自己的个人网页。你将了解到如何轻松绕过常见的技术阻碍,在腾讯云HAI平台上快速部署DeepSeek模型,仅需简单几步,就能获取一个包含个人简介、技能特长、项目经历及联系方式等核心板块的响应式网页。 目录 一、DeepSeek模型部署在腾讯云HAI 二、设计个人网页 一、DeepSeek模型部署在腾讯云HAI 把 DeepSeek 模型部署于腾讯云 HAI,用户便能避开官网访问限制,直接依托腾讯云 HAI 的超强算力运行 DeepSeek-R1 等模型。这一举措不仅降低了技术门槛,还缩短了部署时间,削减了成本。尤为关键的是,凭借 HAI 平台灵活且可扩展的特性,用户能够依据自身特定需求定制专属解决方案,进而更出色地适配特定业务场景,满足各类技术要求 。 点击访问腾讯云HAI控制台地址: 算力管理 - 高性能应用服务 - 控制台 腾讯云高性能应用服务HAI已支持DeepSeek-R1模型预装环境和CPU算力,只需简单的几步就能调用DeepSeek - R1

By Ne0inhk
如何通过 3 个简单步骤在 Windows 上本地运行 DeepSeek

如何通过 3 个简单步骤在 Windows 上本地运行 DeepSeek

它是免费的——社区驱动的人工智能💪。         当 OpenAI 第一次推出定制 GPT 时,我就明白会有越来越多的人为人工智能做出贡献,并且迟早它会完全由社区驱动。         但从来没有想过它会如此接近😂让我们看看如何在 Windows 机器上完全免费使用第一个开源推理模型!  步骤 0:安装 Docker 桌面         我确信很多人已经安装了它,所以可以跳过,但如果没有 — — 这很简单,只需访问Docker 的官方网站,下载并运行安装 👍         如果您需要一些特定的设置,例如使用 WSL,那么有很多指导视频,请查看!我将继续下一步。 步骤 1:安装 CUDA 以获得 GPU 支持         如果您想使用 Nvidia 显卡运行 LLM,则必须安装 CUDA 驱动程序。(嗯……是的,它们需要大量的计算能力)         打开CUDA 下载页面,

By Ne0inhk
在 VSCode 中本地运行 DeepSeek,打造强大的私人 AI

在 VSCode 中本地运行 DeepSeek,打造强大的私人 AI

本文将分步向您展示如何在本地安装和运行 DeepSeek、使用 CodeGPT 对其进行配置以及开始利用 AI 来增强您的软件开发工作流程,所有这些都无需依赖基于云的服务。  步骤 1:在 VSCode 中安装 Ollama 和 CodeGPT         要在本地运行 DeepSeek,我们首先需要安装Ollama,它允许我们在我们的机器上运行 LLM,以及CodeGPT,它是集成这些模型以提供编码辅助的 VSCode 扩展。 安装 Ollama Ollama 是一个轻量级平台,可以轻松运行本地 LLM。 下载Ollama 访问官方网站:https://ollama.com * 下载适合您的操作系统(Windows、macOS 或 Linux)的安装程序。 * 验证安装 安装后,打开终端并运行: ollama --version  如果 Ollama 安装正确,

By Ne0inhk