C++17 信号量模拟实现

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(); // 唤醒所有线程
    }
    

C语言的信号量

#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>sem_t sem;
int shared_counter = 0;void* thread_func(void* arg) {for (int i = 0; i < 100000; i++) {sem_wait(&sem);       // P操作shared_counter++;     // 临界区sem_post(&sem);       // V操作}return NULL;
}int main() {// 初始化信号量(初始值为1)if (sem_init(&sem, 0, 1) != 0) {perror("sem_init failed");return 1;}pthread_t t1, t2;// 创建线程1if (pthread_create(&t1, NULL, thread_func, NULL) != 0) {perror("pthread_create t1 failed");return 1;}// 创建线程2if (pthread_create(&t2, NULL, thread_func, NULL) != 0) {perror("pthread_create t2 failed");return 1;}// 等待线程结束pthread_join(t1, NULL);pthread_join(t2, NULL);printf("Final counter value: %d\n", shared_counter);  // 正确应为200000// 销毁信号量sem_destroy(&sem);return 0;
}

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

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

相关文章

C++ 网络层接口设计与实现:基于 Socket 编程

在网络编程中&#xff0c;网络层是 OSI 七层模型中负责将数据从源节点传输到目的节点的关键层次。在 C 中&#xff0c;网络层的功能通常通过 Socket 编程接口来实现。Socket 提供了一种抽象机制&#xff0c;允许应用程序通过网络发送和接收数据。本文将详细介绍如何在 C 中使用…

uniapp中uni-easyinput 使用@input 不改变绑定的值

只允许输入数字和字母 使用input 正则replace后赋值给A 遇到问题: 当输入任意连续的非法字符时, 输入框不变. 直到输入一个合法字符非法字符才成功被过滤. <uni-forms-item label"纳税人识别号" name"number"><uni-easyinput v-model"numb…

Docker安装hoppscotch

Docker安装hoppscotch 1 简介 1.1 Hoppscotch‌系统 ‌Hoppscotch‌是一个轻量、高效的[API开发生态系统&#xff0c;开源于2020年&#xff0c;原名Postwoman&#xff0c;后更名为Hoppscotch。它基于Node.js构建&#xff0c;支持多种HTTP请求方法&#xff0c;包括GET、POST、…

1.Axum 与 Tokio:异步编程的完美结合

摘要 深入解析 Axum 核心架构与 Tokio 异步运行时的集成&#xff0c;掌握关键原理与实践技巧。 一、引言 在当今的软件开发领域&#xff0c;高并发和高性能是衡量一个系统优劣的重要指标。对于 Web 服务器而言&#xff0c;能够高效地处理大量并发请求是至关重要的。Rust 语言…

CSS伪元素

伪元素 伪元素 用于在元素的内容前后或特定部分插入虚拟元素&#xff0c;并为其添加样式&#xff0c;无需修改 HTML 结构。 语法&#xff1a;使用双冒号 ::&#xff08;现代规范&#xff09; 以下是一些常见的CSS伪元素的示例&#xff1a; 1.::before &#xff1a; 在元素内…

easyexcel使用模板填充excel坑点总结

1.单层map设置值是{属性}&#xff0c;那使用两层map进行设置值&#xff0c;是不是可以使用{属性.属性}&#xff0c;以为取出map里字段只用{属性}就可以设置值&#xff0c;那再加个.就可以从里边map取出对应属性&#xff0c;没有两层map写法 填充得到的文件打开报错 was empty (…

在Ubuntu服务器上部署xinference

一、拉取镜像 docker pull xprobe/xinference:latest二、启动容器&#xff08;GPU&#xff09; docker run -d --name xinference -e XINFERENCE_MODEL_SRCmodelscope -p 9997:9997 --gpus all xprobe/xinference:latest xinference-local -H 0.0.0.0 # 启动一个新的Docker容…

三周年创作纪念日

文章目录 回顾与收获三年收获的五个维度未来的展望致谢与呼唤 亲爱的社区朋友们&#xff0c;大家好&#xff01; 今天是 2025 年 4 月 14 日&#xff0c;距离我在 2022 年 4 月 14 日发布第一篇技术博客《SonarQube 部署》整整 1,095 天。在这条创作之路上&#xff0c;我既感慨…

Redis——五种数据类型

目录 前言 1.String 1.1RAW编码 1.2EMBSTR编码 1.3 INT编码 2.List 3.Set 3.1 InSet编码转化成Dict编码 4.ZSet 4.1结合SkipList和HT实现 4.2使用ZipList实现 4.3编码转换 4.4 ZipList排序功能 5.Hash 5.1Hash底层存储结构 6.Redis数据结构和数据类型关系图 前言…

zookeeper启动报错have small server identifier

解决方案&#xff1a; 1、查看myid是否有重复 2、查看server.X 与myid的X是否一致 3、启动顺序为myid从小到大的服务器顺序

#Linux动态大小裁剪以及包大小变大排查思路

1 动态库裁剪 库分为动态库和静态库&#xff0c;动态库是在程序运行时才加载&#xff0c;静态库是在编译时就加载到程序中。动态库的大小通常比静态库小&#xff0c;因为动态库只包含了程序需要的函数和数据&#xff0c;而静态库则包含了所有的函数和数据。静态库可以理解为引入…

消息队列生产者投递的高可靠性与一致性保障方案

在构建高可靠分布式系统时&#xff0c;确保业务数据库与消息队列&#xff08;MQ&#xff09;之间的一致性是一项核心挑战。尤其当使用 Kafka 作为消息队列中间件时&#xff0c;如何避免“数据库写入成功&#xff0c;但消息发送失败”或“消息重复发送”等问题&#xff0c;成为系…

Formality:Bug记录

相关阅读 Formalityhttps://blog.csdn.net/weixin_45791458/category_12841971.html?spm1001.2014.3001.5482 本文记录博主在使用Synopsys的形式验证工具Formality中遇到的一个Bug。 Bug复现 情况一 // 例1 module dff (input clk, input d_in, output d_out …

通信算法之267 : DJI无人机 云哨 DroneID 640ms

DJI 无人机 与DroneID 转 *** 载 0x01 摘要 消费级无人机可以用于高级航拍、物流和人道主义救援等等。但是其广泛使用给安全、安保和隐私带来了许多风险。例如&#xff0c;攻击方可能会使用无人机进行监视、运输非法物品&#xff0c;或通过侵入机场上方的封闭空域造成经济损…

论坛测试报告

作者前言 &#x1f382; ✨✨✨✨✨✨&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f382; ​&#x1f382; 作者介绍&#xff1a; &#x1f382;&#x1f382; &#x1f382; &#x1f389;&#x1f389;&#x1f389…

npx 的作用以及延伸知识(.bin目录,npm run xx 执行)

文章目录 前言原理解析1. npx 的作用2. 为什么会有 node_modules/.bin/lerna3. npx 的查找顺序4. 执行流程总结1&#xff1a; 1. .bin 机制什么是 node_modules/.bin&#xff1f;例子 2. npx 的底层实现npx 是如何工作的&#xff1f;为什么推荐用 npx&#xff1f;npx 的特殊能力…

【c语言】深入理解指针3——回调函数

一、回调函数 回调函数&#xff1a;通过函数指针调用的函数. 当把一个函数的地址传递给另一个函数&#xff0c;通过该地址去调用其指向的函数&#xff0c;那么这个被调用的函数就是回调函数. 示例&#xff1a; 在【深入理解指针2】中结尾写了用函数指针实现计算器的功能&#…

HTTP 核心概念

&#x1f9d1; 博主简介&#xff1a;CSDN博客专家&#xff0c;历代文学网&#xff08;PC端可以访问&#xff1a;https://literature.sinhy.com/#/literature?__c1000&#xff0c;移动端可微信小程序搜索“历代文学”&#xff09;总架构师&#xff0c;15年工作经验&#xff0c;…

VidBot:从野外 2D 人体视频中学习可泛化的 3D 动作,实现零样本机器人操控

25年3月来自慕尼黑工大、瑞士 ETH 和微软的论文“VidBot: Learning Generalizable 3D Actions from In-the-Wild 2D Human Videos for Zero-Shot Robotic Manipulation”。 未来的机器人被设想为能够执行各种家务的多功能系统。最大的问题仍然是&#xff0c;如何在尽量减少机器…

Linux 日常运维命令大全

Linux 作为一种开源操作系统&#xff0c;在服务器运维中扮演着重要角色。掌握常用的 Linux 命令对于运维人员而言至关重要。本文将整理一份 Linux 服务器运维常用命令大全&#xff0c;帮助你在日常工作中提高效率和准确性。 1. 基础命令 基础命令是Linux操作的起点&#xff0…