C++17 信号量模拟实现

news2025/4/19 8:54:23

C++17 信号量模拟实现

一、实现原理

C++17 标准库没有原生信号量(C++20才有),但可以通过 std::mutex + std::condition_variable 模拟实现。以下是核心逻辑:

#include <mutex>
#include <condition_variable>

class CountingSemaphore {
private:
    int count_;                  // 当前可用资源数
    std::mutex mutex_;           // 保护计数器的锁
    std::condition_variable cv_; // 阻塞/唤醒线程

public:
    explicit CountingSemaphore(int initial = 0) : count_(initial) {}

    // P操作:请求资源(计数器减1)
    void acquire() {
        std::unique_lock<std::mutex> lock(mutex_);
        cv_.wait(lock, [this] { return count_ > 0; }); // 等待资源可用
        --count_;
    }

    // V操作:释放资源(计数器加1)
    void release() {
        std::lock_guard<std::mutex> lock(mutex_);
        ++count_;
        cv_.notify_one(); // 唤醒一个等待线程
    }
};
二、完整可编译代码
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <queue>
#include <chrono>

// 信号量实现类
class CountingSemaphore {
private:
    int count_;
    std::mutex mutex_;
    std::condition_variable cv_;
public:
    explicit CountingSemaphore(int initial = 0) : count_(initial) {}

    void acquire() {
        std::unique_lock<std::mutex> lock(mutex_);
        cv_.wait(lock, [this] { return count_ > 0; });
        --count_;
    }

    void release() {
        std::lock_guard<std::mutex> lock(mutex_);
        ++count_;
        cv_.notify_one();
    }
};

// 全局资源
constexpr int MAX_BUFFER = 5;
CountingSemaphore empty_slots(MAX_BUFFER); // 初始空位
CountingSemaphore data_items(0);           // 初始数据项
std::mutex buffer_mutex;                   // 保护缓冲区
std::queue<int> buffer;                     // 共享缓冲区
bool producer_done = false;                // 生产结束标志

// 生产者函数
void producer() {
    for (int i = 1; i <= 10; ++i) {
        empty_slots.acquire(); // 等待空位
        
        {
            std::lock_guard<std::mutex> lock(buffer_mutex);
            buffer.push(i);
            std::cout << "生产者添加: " << i << std::endl;
        }
        
        data_items.release(); // 增加数据项
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
    }
    
    // 标记生产完成
    std::lock_guard<std::mutex> lock(buffer_mutex);
    producer_done = true;
}

// 消费者函数
void consumer() {
    while (true) {
        data_items.acquire(); // 等待数据项
        
        {
            std::lock_guard<std::mutex> lock(buffer_mutex);
            if (producer_done && buffer.empty()) break; // 退出条件
            
            int val = buffer.front();
            buffer.pop();
            std::cout << "消费者取出: " << val << std::endl;
        }
        
        empty_slots.release(); // 释放空位
        std::this_thread::sleep_for(std::chrono::milliseconds(150));
    }
}

int main() {
    std::thread t1(producer);
    std::thread t2(consumer);
    
    t1.join();
    t2.join();
    
    std::cout << "程序正常退出" << std::endl;
    return 0;
}
三、编译与运行
  1. 编译命令(需要支持C++17的编译器):

    g++ -std=c++17 -pthread -o semaphore_demo semaphore_demo.cpp
    
  2. 运行输出示例

四、关键机制解析
组件作用
std::mutex保护共享计数器 count_ 的线程安全访问
std::condition_variable实现阻塞等待和唤醒机制
cv_.wait()阻塞线程直到资源可用(count_ > 0
cv_.notify_one()资源释放后唤醒一个等待线程
五、注意事项
  1. 虚假唤醒处理cv_.wait() 必须使用循环判断条件

    cv_.wait(lock, [this] { return count_ > 0; }); // 正确写法
    
  2. 死锁避免:确保每次 acquire() 都有对应的 release()

  3. 性能优化:高频场景下可改用 notify_all() + 线程竞争

    void release() {
        std::lock_guard<std::mutex> lock(mutex_);
        ++count_;
        cv_.notify_all(); // 唤醒所有线程
    }
    

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2337858.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

web后端语言中篇

#作者&#xff1a;允砸儿 #日期&#xff1a;乙巳青蛇年 三月十八 笔者本来打算隔一天给它更完的&#xff0c;但是事情有点多这几天&#xff0c;实在是抱歉。废话不多说直接进入正题。 PHP流程控制语句 什么是流控:流程控制语句用于决定代码的执行顺序。 #注意流程控制语句…

Spine-Leaf 与 传统三层架构:全面对比与解析

本文将详细介绍Spine-Leaf架构&#xff0c;深入对比传统三层架构&#xff08;Core、Aggre、Access&#xff09;&#xff0c;并探讨其与Full-mesh网络和软件定义网络&#xff08;SDN&#xff09;的关联。通过通俗易懂的示例和数据中心网络分析&#xff0c;我将帮助您理解Spine-L…

Vmware esxi 查看硬盘健康状况

起因 硬盘掉盘 - - 使用自带的命令esxcli 列出所有硬盘 esxcli storage core device list[rootlocalhost:~] esxcli storage core device list t10.NVMe____INTEL_MEMPEK1W016GAL____________________PHBT83660BYP016D____00000001Display Name: Local NVMe Disk (t10.NVMe…

4.黑马学习笔记-SpringMVC(P43-P47)

1.SpringMVC简介 SpringMVC技术&#xff08;更少的代码&#xff0c;简便&#xff09;与servlet技术功能相同&#xff0c;属于web层开发技术。 SpringMVC是一种基于java实现MVC模型的轻量级web框架。 轻量级指的是&#xff08;内存占用比较低&#xff0c;运行效率高&#xff09;…

【文件操作与IO】详细解析文件操作与IO (一)

本篇博客给大家带来的是文件操作的知识点. &#x1f40e;文章专栏: JavaEE初阶 &#x1f680;若有问题 评论区见 ❤ 欢迎大家点赞 评论 收藏 分享 如果你不知道分享给谁,那就分享给薯条. 你们的支持是我不断创作的动力 . 王子,公主请阅&#x1f680; 要开心要快乐顺便进步 一. …

PMP考试费能报销吗?报销流程是什么?

最近也是到了6月和8月PMP考试的报名高峰期&#xff0c;后台有小伙伴最常问的问题就是&#xff0c;PMP考试费比较贵&#xff0c;能不能报销&#xff1f;报销流程是什么&#xff1f; 先给大家分享一下最新PMP报名消息和考试信息&#xff1a; 添加图片注释&#xff0c;不超过 140…

学习threejs,使用EffectComposer后期处理组合器(采用RenderPass、GlitchPass渲染通道)

&#x1f468;‍⚕️ 主页&#xff1a; gis分享者 &#x1f468;‍⚕️ 感谢各位大佬 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍⚕️ 收录于专栏&#xff1a;threejs gis工程师 文章目录 一、&#x1f340;前言1.1 ☘️THREE.EffectComposer 后期…

docker部署springboot(eureka server)项目

打jar包 使用maven&#xff1a; <build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><configuration><source>17</source><target>17&…

第 7 期:DDPM 采样提速方案:从 DDPM 到 DDIM

本期关键词:采样加速、DDIM 推导、可控性提升、伪逆过程、代码实战 前情回顾:DDPM 的采样瓶颈 在前几期中,我们构建了一个完整的 DDPM 生成流程。但是你可能已经发现: 生成一张图像太慢了!!! 原因是: DDPM 要在 T 个时间步中一步步地去噪,从 x_T → x_0。而通常 T 至…

1panel第三方应用商店(本地商店)配置和使用

文章目录 引言资源网站实战操作说明 引言 1Panel 提供了一个应用提交开发环境&#xff0c;开发者可以通过提交应用的方式将自己的应用推送到 1Panel 的应用商店中&#xff0c;供其他用户使用。由此衍生了一种本地应用商店的概念&#xff0c;用户可以自行编写应用配置并上传到自…

七牛使用任务工作流对音频进行转码

最近工作中有对音频转码的需求&#xff0c;比如 iOS 设备中对 ogg 格式的语音支持力度不够&#xff0c;那么可以讲ogg转码成mp3格式,下面来介绍一下&#xff0c;如果通过七牛&#xff0c;后端自行转码&#xff0c;不需要前端做任何事情。 假设我们存在一个音频的 url&#xff…

基于springBoot+vue的PC 端学习系统(源码+lw+部署文档+讲解),源码可白嫖!

摘要 随着我国经济的高速发展与人们生活水平的日益提高&#xff0c;人们对生活质量的追求也多种多样。尤其在人们生活节奏不断加快的当下&#xff0c;人们更趋向于足不出户解决生活上的问题&#xff0c;线上管理系统展现了其蓬勃生命力和广阔的前景。与此同时&#xff0c;在疫…

【Python爬虫基础篇】--1.基础概念

目录 1.爬虫--定义 2.爬虫--组成 3.爬虫--URL 1.爬虫--定义 网络爬虫&#xff0c;是一种按照一定规则&#xff0c;自动抓取互联网信息的程序或者脚本。另外一些不常使用的名字还有蚂蚁、自动索引、模拟程序或者蠕虫。随着网络的迅速发展&#xff0c;万维网成为大量信息的载体…

CSS进度条带斑马纹动画(有效果图)

效果图 .wxml <view class"tb"><view class"tb-line" style"transform:translateX({{w%}})" /> </view> <button bind:tap"updateLine">增加进度</button>.js Page({data: {w:0,},updateLine(){this.…

HarmonyOS:使用Refresh组件实现页面下拉刷新上拉加载更多

一、前言 可以进行页面下拉操作并显示刷新动效的容器组件。 说明 该组件从API Version 8开始支持。后续版本如有新增内容&#xff0c;则采用上角标单独标记该内容的起始版本。该组件从API Version 12开始支持与垂直滚动的Swiper和Web的联动。当Swiper设置loop属性为true时&…

【C++深入系列】:模版详解(上)

&#x1f525; 本文专栏&#xff1a;c &#x1f338;作者主页&#xff1a;努力努力再努力wz &#x1f4aa; 今日博客励志语录&#xff1a; 你不需要很厉害才能开始&#xff0c;但你需要开始才能很厉害。 ★★★ 本文前置知识&#xff1a; 类和对象&#xff08;上&#xff09; …

leetcode刷题日记——同构字符串

[ 题目描述 ]&#xff1a; [ 思路 ]&#xff1a; 题目要求判断 s 和 t 是否为同构字符串&#xff0c;即 s 中每个字符与 t 中对应位置的字符形成一个映射关系&#xff0c;且只能是一对一映射ASCII&#xff08;American Standard Code for Information Interchange&#xff09…

HTTP/1.1 队头堵塞问题

文章目录 一、队头堵塞1、非管线化2、管线化 二、如何解决&#xff1f; 一、队头堵塞 1、非管线化 如图&#xff0c;http 请求必须等到上一个请求响应后才能发送&#xff0c;后面的以此类推&#xff0c;由此可以看出&#xff0c;在一个 tcp 通道中&#xff0c;如果某个 http 请…

【Quest开发】在虚拟世界设置具有遮挡关系的透视窗口

软件&#xff1a;Unity 2022.3.51f1c1、vscode、Meta XR All in One SDK V72 硬件&#xff1a;Meta Quest3 仅针对urp管线 参考了YY老师这篇&#xff0c;可以先看他的再看这个可能更好理解一些&#xff1a;Unity Meta Quest MR 开发&#xff08;七&#xff09;&#xff1a;使…

常用 Git 命令详解

Git 是一个强大的版本控制工具&#xff0c;广泛用于软件开发和团队协作中。掌握 Git 命令可以帮助开发者更高效地管理代码版本和项目进度。本文将介绍一些常用的 Git 命令&#xff0c;并提供示例以帮助你更好地理解和应用这些命令。 目录 常用命令 git clonegit stashgit pul…