0

0

C++如何实现信号量同步?(跨平台semaphore封装)

裘德小鎮的故事

裘德小鎮的故事

发布时间:2026-03-12 14:29:34

|

138人浏览过

|

来源于php中文网

原创

能,但需编译器和标准库支持:gcc 11+、clang 12+、msvc 16.11+ 配合 -std=c++20;否则需手写基于 mutex+condition_variable 的跨平台封装。

c++如何实现信号量同步?(跨平台semaphore封装)

std::counting_semaphore 在 C++20 里能直接用吗?

能,但得看编译器和标准库是否真正支持——std::counting_semaphore 是 C++20 标准的一部分,但 GCC 11+(需 -std=c++20)和 Clang 12+ 才开始提供可用实现;MSVC 2019 16.11+ 也支持,但早期版本返回编译错误 error: 'counting_semaphore' is not a member of 'std'

常见错误现象:代码写了 std::counting_semaphore sem;,却在 GCC 10 或 Clang 11 下编译失败;或者链接时报 undefined reference to 'std::counting_semaphore::counting_semaphore()'——这通常是因为 libstdc++/libc++ 没更新到带完整同步原语的版本。

  • 确认编译器版本:运行 g++ --versionclang++ --version
  • 启用 C++20:必须加 -std=c++20-std=gnu++20 也可,但避免隐式扩展)
  • 检查标准库:GCC 用户建议升级到 11.2+,Clang 用户搭配 libc++ 12+ 更稳妥
  • Windows 上 MSVC 需 16.11+ 且项目属性中设置 Language Standard = ISO C++20 Standard

Windows 和 Linux 上如何手写跨平台 semaphore 封装?

核心思路是用条件变量 + 互斥量模拟计数信号量行为,不依赖系统 API(如 sem_waitCreateSemaphore),从而规避 POSIX vs Win32 的头文件、链接、语义差异。

关键难点在于:wait() 必须可中断(比如被 std::this_thread::interrupt() 停止)、支持超时、且不能因虚假唤醒导致计数错乱。

立即学习C++免费学习笔记(深入)”;

  • 内部用 std::mutex 保护计数器 m_count
  • wait() 中循环检查 m_count > 0,用 cv.wait(lock, [&]{ return m_count > 0; }) 防止虚假唤醒
  • try_wait_for() 必须用 cv.wait_for(lock, rel_time, [&]{ return m_count > 0; }),否则超时逻辑失效
  • Windows 下若想用原生 HANDLE 实现,要记得调用 CloseHandle 清理,否则泄漏句柄

示例片段(简化版):

百宝箱
百宝箱

百宝箱是支付宝推出的一站式AI原生应用开发平台,无需任何代码基础,只需三步即可完成AI应用的创建与发布。

下载
class semaphore {
    std::mutex m_mutex;
    std::condition_variable m_cv;
    long m_count;
public:
    explicit semaphore(long count = 0) : m_count(count) {}
    void release() {
        std::lock_guard<std::mutex> lock(m_mutex);
        ++m_count;
        m_cv.notify_one();
    }
    void acquire() {
        std::unique_lock<std::mutex> lock(m_mutex);
        m_cv.wait(lock, [&]{ return m_count > 0; });
        --m_count;
    }
};

为什么不要直接封装 POSIX sem_* 函数?

因为 sem_wait/sem_post 在 macOS 上已废弃(自 macOS 10.15 起标记为 DEPRECATED),链接时可能报 ld: warning: object file was built for newer macOS version;而 Linux 上虽可用,但语义与 C++ 标准线程模型不完全对齐——比如它不感知 std::thread 的中断点,也不兼容 std::jthread 的自动 join。

更隐蔽的问题是:POSIX 信号量默认是进程间(pshared=1)或进程内(pshared=0),但 C++ 多线程场景下你几乎永远只需要进程内语义;一旦误设 pshared=1,在 macOS 或部分嵌入式 libc 上会直接失败并返回 ENOSYS

  • macOS 不支持 sem_init(返回 ENOSYS),只能用 dispatch_semaphore_t 替代,但那是 Objective-C runtime 依赖
  • Android NDK r21+ 才开始提供有限 sem_* 支持,旧版本直接不可用
  • 静态链接 musl libc(如 Alpine Linux)时,sem_wait 可能因缺少 pthread 实现而卡死

release() 和 acquire() 的参数与异常安全怎么处理?

std::counting_semaphore::acquire() 不抛异常,但手写封装里如果 cv.waitstd::thread::interrupt() 中断,会抛 std::thread::interrupted——这不符合信号量“只阻塞/计数”的契约。

所以实际封装中,要么屏蔽中断(不推荐),要么把中断转为返回 false(对应 try_acquire),或统一用 std::terminate(严格对标标准库行为)。

  • 标准库 std::counting_semaphore::try_acquire_for 返回 bool,超时即返回 false,不抛异常
  • 手写版本若支持中断,应在文档里明确标注:“调用线程需保证未被中断,否则行为未定义”
  • 所有修改 m_count 的操作必须在锁区内完成,且 release() 中的 ++m_countnotify_one() 之间不能被抢占,否则可能丢通知
  • 避免在构造函数里做耗时操作(如创建内核对象),否则 semaphore sem(1); 可能在栈上初始化失败

跨平台信号量最难缠的不是功能实现,而是不同系统对“等待被取消”的定义不一致:Linux 认为 pthread_cond_wait 可被信号中断,Windows 的 WaitForSingleObject 却不响应 Ctrl+C——这些细节不暴露给上层,就容易在热更新或服务重启时卡死线程。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
scripterror怎么解决
scripterror怎么解决

scripterror的解决办法有检查语法、文件路径、检查网络连接、浏览器兼容性、使用try-catch语句、使用开发者工具进行调试、更新浏览器和JavaScript库或寻求专业帮助等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

492

2023.10.18

500error怎么解决
500error怎么解决

500error的解决办法有检查服务器日志、检查代码、检查服务器配置、更新软件版本、重新启动服务、调试代码和寻求帮助等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

382

2023.10.25

scripterror怎么解决
scripterror怎么解决

scripterror的解决办法有检查语法、文件路径、检查网络连接、浏览器兼容性、使用try-catch语句、使用开发者工具进行调试、更新浏览器和JavaScript库或寻求专业帮助等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

492

2023.10.18

500error怎么解决
500error怎么解决

500error的解决办法有检查服务器日志、检查代码、检查服务器配置、更新软件版本、重新启动服务、调试代码和寻求帮助等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

382

2023.10.25

堆和栈的区别
堆和栈的区别

堆和栈的区别:1、内存分配方式不同;2、大小不同;3、数据访问方式不同;4、数据的生命周期。本专题为大家提供堆和栈的区别的相关的文章、下载、课程内容,供大家免费下载体验。

443

2023.07.18

堆和栈区别
堆和栈区别

堆(Heap)和栈(Stack)是计算机中两种常见的内存分配机制。它们在内存管理的方式、分配方式以及使用场景上有很大的区别。本文将详细介绍堆和栈的特点、区别以及各自的使用场景。php中文网给大家带来了相关的教程以及文章欢迎大家前来学习阅读。

605

2023.08.10

线程和进程的区别
线程和进程的区别

线程和进程的区别:线程是进程的一部分,用于实现并发和并行操作,而线程共享进程的资源,通信更方便快捷,切换开销较小。本专题为大家提供线程和进程区别相关的各种文章、以及下载和课程。

765

2023.08.10

Python 多线程与异步编程实战
Python 多线程与异步编程实战

本专题系统讲解 Python 多线程与异步编程的核心概念与实战技巧,包括 threading 模块基础、线程同步机制、GIL 原理、asyncio 异步任务管理、协程与事件循环、任务调度与异常处理。通过实战示例,帮助学习者掌握 如何构建高性能、多任务并发的 Python 应用。

377

2025.12.24

C# ASP.NET Core微服务架构与API网关实践
C# ASP.NET Core微服务架构与API网关实践

本专题围绕 C# 在现代后端架构中的微服务实践展开,系统讲解基于 ASP.NET Core 构建可扩展服务体系的核心方法。内容涵盖服务拆分策略、RESTful API 设计、服务间通信、API 网关统一入口管理以及服务治理机制。通过真实项目案例,帮助开发者掌握构建高可用微服务系统的关键技术,提高系统的可扩展性与维护效率。

76

2026.03.11

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
C# 教程
C# 教程

共94课时 | 11.1万人学习

C 教程
C 教程

共75课时 | 5.3万人学习

C++教程
C++教程

共115课时 | 21.4万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号