C++每日训练 Day 15:构建线程池支持的事件中心 SignalHub

3dMax脚本类插件:不写 C++也能造“原生”工具 从plugin关键字到场景持久化,拆解 MAXScript 插件开发全流程! 阅读详情

📘 本篇在前几日协程 + 信号 + 调度器的基础上,构建一个支持多线程调度、事件分发和协程 resume 的通用事件中心:SignalHub。通过接入线程池和事件标识,我们实现一个真正可以落地于大型应用(如服务端、UI 框架、游戏引擎)的异步事件调度系统。篇末提供问题与答案帮助巩固理解。


🔁 回顾 Day 14:信号调度器 + 协程 resume

组件名称作用
Dispatcher可执行任务调度接口
ThreadDispatcher线程任务队列,支持 submit + 主动 run
AwaiterWithDispatcher协程 resume 时使用 dispatcher 进行线程切换
AsyncSignalWithDispatcheremit 时自动 resume 挂起协程到指定线程

🎯 今日目标:

模块作用说明
ThreadPool支持多线程任务提交与执行
SignalHub注册不同类型事件 + 标识符,统一事件中心接口
AwaitableHub支持协程挂起 + resume 到线程池
泛型接口支持任意参数/类型事件统一封装

在这里插入图片描述

✅ 一、构建线程池(简易版)

class ThreadPool : public Dispatcher {
public:
    ThreadPool(size_t count = std::thread::hardware_concurrency()) : running(true) {
        for (size_t i = 0; i < count; ++i) {
            workers.emplace_back([this]() {
                while (true) {
                    std::function<void()> task;
                    {
                        std::unique_lock<std::mutex> lock(mtx);
                        cv.wait(lock, [&]() { return !tasks.empty() || !running; });
                        if (!running && tasks.empty()) return;
                        task = std::move(tasks.front());
                        tasks.pop();
                    }
                    task();
                }
            });
        }
    }

    void dispatch(std::function<void()> task) override {
        {
            std::lock_guard<std::mutex> lock(mtx);
            tasks.push(std::move(task));
        }
        cv.notify_one();
    }

    void stop() {
        running = false;
        cv.notify_all();
        for (auto& t : workers) t.join();
    }

private:
    std::vector<std::thread> workers;
    std::queue<std::function<void()>> tasks;
    std::mutex mtx;
    std::condition_variable cv;
    bool running;
};

✅ 二、事件中心 SignalHub 结构

template<typename Key, typename T>
class SignalHub {
public:
    using Callback = std::function<void(const T&)>;

    void subscribe(const Key& key, Callback cb) {
        observers[key].push_back(std::move(cb));
    }

    void emit(const Key& key, const T& value) {
        auto it = observers.find(key);
        if (it != observers.end()) {
            for (auto& cb : it->second) cb(value);
        }
    }

private:
    std::unordered_map<Key, std::vector<Callback>> observers;
};

✅ 三、接入 Awaiter + resume to ThreadPool

template<typename T>
struct HubAwaiter {
    std::optional<T> result;
    std::coroutine_handle<> handle;
    Dispatcher* dispatcher;

    bool await_ready() const noexcept { return false; }
    void await_suspend(std::coroutine_handle<> h) { handle = h; }
    T await_resume() { return *result; }

    void resume(T value) {
        result = std::move(value);
        dispatcher->dispatch([h = handle]() { h.resume(); });
    }
};

✅ 四、构建 AwaitableSignalHub

template<typename Key, typename T>
class AwaitableSignalHub {
public:
    AwaitableSignalHub(Dispatcher* d) : dispatcher(d) {}

    void emit(const Key& key, const T& value) {
        auto it = waiters.find(key);
        if (it != waiters.end()) {
            for (auto* w : it->second) w->resume(value);
            waiters.erase(it);
        }
    }

    HubAwaiter<T>* wait(const Key& key) {
        auto* w = new HubAwaiter<T>();
        w->dispatcher = dispatcher;
        waiters[key].push_back(w);
        return w;
    }

private:
    Dispatcher* dispatcher;
    std::unordered_map<Key, std::vector<HubAwaiter<T>*>> waiters;
};

✅ 五、实战示例:登录事件中心

Task<void> waitLogin(AwaitableSignalHub<std::string, std::string>& hub) {
    std::string user = co_await *hub.wait("login");
    std::cout << "用户登录成功: " << user << std::endl;
    co_return;
}
int main() {
    ThreadPool pool(2);
    AwaitableSignalHub<std::string, std::string> hub(&pool);
    
    auto task = waitLogin(hub); // 协程开始等待登录事件

    std::this_thread::sleep_for(std::chrono::milliseconds(200));
    hub.emit("login", "Jerry"); // 发出登录事件

    std::this_thread::sleep_for(std::chrono::seconds(1));
    pool.stop();
    return 0;
}

输出:

用户登录成功: Jerry

📘 巩固练习题(附答案)

Q1:SignalHub 和 AwaitableSignalHub 的区别?
A:SignalHub 是同步事件派发,AwaitableSignalHub 是协程挂起后由 emit 异步 resume。

Q2:Dispatcher 的作用?
A:控制协程在哪个线程 resume,是实现线程调度 resume 的关键。

Q3:为什么使用 optional 而不是直接变量?
A:optional 能很好表达“结果未设置”状态,避免未初始化访问。

Q4:emit 后协程 resume 会在哪里运行?
A:取决于 awaiter 中 dispatcher 设置的线程,一般是主线程或线程池。

Q5:是否支持多个事件 key?
A:是的,每种 key 类型对应一组 waiter 或 callback,完全解耦。


🔭 下一步预告:构建 GUI + 事件驱动架构系统(Day 16)

  • 将信号中心 + 协程结合 GUI 主线程(事件循环)
  • 构建响应式组件(按钮、输入框)事件信号
  • 支持主线程 resume + 业务线程分发

📌 若你有 UI 框架、游戏引擎、微服务等场景,我可以帮助你落地完整架构 💡

GD32F470实战:如何用EXMC+SDRAM搞定LVGL大内存需求(附完整代码) 本文详细介绍了如何在GD32F470微控制器上通过EXMC接口驱动外部SDRAM,以满足LVGL图形库的大内存需求。内容涵盖硬件连接、EXMC时序配置、轻量级内存管理器实现,以及将LVGL帧缓冲和资源无缝集成到SDRAM的实战方法,并提供了完整的代码示例和调试技巧,帮助开发者构建稳定可靠的嵌入式GUI系统。 阅读详情

相关推荐

模拟波束赋形 vs 数字波束赋形:5G天线设计中的成本与性能权衡

本文深入剖析了5G天线设计中模拟波束赋形与数字波束赋形的核心差异,重点探讨了二者在硬件成本、功耗、网络覆盖能力和频谱效率等方面的权衡。文章指出,模拟方案成本低但灵活性有限,数字方案性能强大但代价高昂,而混合波束赋形已成为主流折中方案。决策需紧密结合具体的部署场景、性能要求和总拥有成本进行综合考量。

weixin_28432777的博客 840

C++每日训练 Day 15:构建线程池支持的事件中心 SignalHub(易学版)

);📌 说明:这个类负责让协程挂起,并在合适的时候 resume。关键组件作用ThreadPool处理异步任务的线程池Dispatcher 接口控制协程 resume 到哪个线程协程挂起点,封装 resume 动作事件中心,支持事件挂起 + resume。

Interview_TC的博客 445

IIS - 会话cookie中缺少HttpOnly属性

不啰嗦,我们直接开始! 先进行常规设置 打开配置编辑器 选中图中节点,将httpOnlyCookies设置为true 然后。。。不起作用。。。 换种方式 通过配置出站规则getcookie添加HttpOnly 在Web.config中添加如下出站规则 <rewrite> <outboundRules> <rule name="Add HttpOnly"> <match serverVa.

。 21万+

选型笔记:Type‑C 扩展坞方案评估|DP1.4 转 HDMI2.0 桥接芯片 GSV2201S@ACP

本文记录 GSV2201S 这款 DP1.4a 转 HDMI2.0 桥接芯片评估,梳理视频、HDCP、PD 集成能力,总结 Type‑C 转接 Dongle、扩展坞方案的适配边界,以及差分走线、PD 时序、HDCP 握手等高频调试问题,为 C 口视频转接方案选型提供参考。

ACP广源盛的博客 213

RTAB-Map实战:用RGB-D相机5步搞定机器人自主建图(附避坑指南)

本文提供了一份详细的RTAB-Map实战指南,通过5个核心步骤,指导用户使用RGB-D相机(如Intel RealSense D435i)快速实现机器人自主建图。内容涵盖环境准备、传感器启动、核心参数配置、实战调优及地图后处理,并重点解析了算法关键参数与常见避坑方案,旨在帮助初学者高效搭建鲁棒的视觉SLAM系统。

g9h0i1的博客 788

ResNet改进(51):基于轴向注意力机制的改进ResNet模型

本文介绍了一种结合轴向注意力机制的改进ResNet模型。 通过在传统CNN中引入轴向注意力,我们能够在保持计算效率的同时,增强模型对图像全局上下文的理解能力。这种混合架构结合了CNN的局部特征提取优势和注意力机制的全局建模能力,在图像识别任务中表现出色。 轴向注意力机制的应用不仅限于ResNet,也可以灵活地集成到其他CNN架构中。 未来可以探索更多注意力变体,或将其应用于特定领域任务,如医学图像分析、自动驾驶等需要精确空间理解的场景。 希望本文能帮助你理解如何将注意力机制与传统CNN结合,为你的计

2401_82355416的博客 357

C++移动构造函数详解

移动构造函数是C++11引入的一种特殊构造函数,用于从临时对象(右值)"窃取"资源来初始化新对象,而不是进行昂贵的深拷贝。char* data;public:// 移动构造函数:参数是右值引用 &&// 关键:把源对象的资源置空。

拒绝AI文,努力在AI时代写出感动人心的好文章 385

巨正则蒙特卡洛—甲烷和二氧化碳的竞争吸附GCMC

巨正则蒙特卡洛—甲烷和二氧化碳的竞争吸附GCMC

Rockyoung 8017

ThunderKittens 深度解析:一个不基于 MLIR 的 C++ 模板 GPU DSL

Triton 是"让更多人能写 GPU kernel",ThunderKittens 是"让会写的人写得更爽"。前者用 MLIR 换易用性,后者用 C++ 模板换可控性。它们不是替代关系,而是覆盖了 GPU 编程光谱的两端。ThunderKittens 的出现,本质上是对"GPU DSL 必须基于 MLIR"这一默认假设的挑战。编译期类型系统本身就可以是一个强大的 DSL 基础设施,不需要中间 IR,不需要黑盒 pass,也能实现 tile 级抽象和零运行时开销。

Maxwell_Newton的博客 310

【YOLO11改进 - 注意力机制】SCSA通过结合空间注意力和通道注意力,提高各种下游视觉任务的性能。

【YOLO11改进 - 注意力机制】SCSA通过结合空间注意力和通道注意力,提高各种下游视觉任务的性能。

专注于图像领域,主要研究内容包括计算机视觉和深度学习,特别是在图像分类、目标检测和图像生成等方面有深入的研究和实践经验。 473

【AI理论学习】手把手推导扩散模型:Diffusion Models(DDPM)

生成扩散模型DDPM如下图所示分为前向、逆向两个过程,它首先通过不断往原始清晰数据中添加噪声使其变成标准高斯噪声(前向过程),而后期望从标准高斯噪声中还原原始数据(逆向过程)。若能实现,那便可从已知的标准高斯分布中采样一个噪声数据,而后利用DDPM模型生成符合原始数据分布的新数据。 Diffusion模型包含两个过程:数学符号:y∝xy\propto xy∝x:y正比于x,即y随着x增大而线性增大。 条件概率:贝叶斯公式:p(A∣B)=p(B∣A)×p(A)p(B)p(A|B)=\frac{p(B|A)\

小哲的博客 1506

[C++]:6.STL:vector

今天就要开始学vector容器,这节课我们主要学习vector中经常会用到的函数,并且给出该函数的模拟实现。话不多说,直接上干货。下面是本章的思维导图,那么,让我们开始吧!

2301_80817679的博客 136

高通8155源码下载与Android源码编译

一.下载代码 高通下载源码位置: Qualcomm CreatePoint 注册账号,然后登入 可以先选择edit -> infotainment -> sa8155 -> favorites, 点击View TOC就可以过滤出sa8155所有的信息目录.如下图 拉取代码链接在ChipCode中,点击进去可以看到所有的基线版本,选择你需要下载的版本点进去,如sa8155p-la-1-0_ap_standard_oem,把高通专有代码和开源代码都下载到本机. 1..

Johnny2004的专栏 7675

蓝桥杯·全国青少年信息素养大赛·电子学会Python/C++考级:倍增法求最近公共祖先LCA解析

蓝桥杯报名正在火热进行(软件赛报名 2026-10-08—12-18,省赛 2027-04-10),电子学会 12 月软件编程(C/C++ 一级至十级、Python 一级至六级)考级报名也已开启(9/23—12/2),全国青少年信息素养大赛算法应用主题赛同步推进——把 LCA 这个模板练熟,正是冲刺这些赛事树型考点的关键一步。所谓「最近公共祖先」:在一棵以某个节点为根的树里,两个节点 u、v 向上追溯,最早交汇的那个祖先就叫它们的 LCA。比如家谱里,你和你表哥最近的共同长辈就是你们的祖父。

IT_Scratch的博客 530

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

C++ 多态详解:作用、原理与实战用法

多态是C++面向对象的核心特性,通过虚函数实现运行期动态绑定,使同一接口在不同对象上表现出不同行为。其核心作用包括统一接口、提升可扩展性、降低耦合、支持运行时分派与批量处理。实现依赖虚函数表(vtable)和虚指针(vptr),需注意虚析构函数、对象切片等陷阱。相比编译期多态(模板),运行期多态灵活性高但有调用开销。合理结合两者,可兼顾代码的灵活性与性能,是构建可扩展、低耦合系统的关键。

2402_85743155的博客 367

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

【C语言】字符与字符串函数

C语言中字符串为以\0结尾的字符数组,标准库函数基于此约定。本文解析字符串内存布局,详解ctype.h与string.h中字符分类、长度计算、拷贝、比较等函数的使用要点,强调长度判断与越界风险,最后通过命令行待办事项示例整合fgets、sscanf、strncpy、strcmp,展示实际应用中的关键细节。

记录在学习过程中学到的知识 334

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

C++ 函数模版

模板参数:● template ,T 就是类型占位符,名字可以自定义,习惯用T● 占位符可以在函数返回值、形参、函数内部当作类型使用使用要点模板代码一般写在头文件:编译器需要看到完整模板源码,才能在每个编译单元完成实例化全局函数、类内成员函数都可以写成模板实例化发生在编译阶段,不是运行时隐式实例化:调用时编译器自动推导实参类型,生成对应版本函数。

2501_94041724的博客 184

C++ 运行时多态与虚函数机制全面剖析【面试重点】

构成C++ 运行时多态必须是基类的指针或者引用调用虚函数。被调用的函数必须是虚函数,并且派生类必须对该虚函数完成重写/覆盖(Override)。只有基类的指针或引用才能既指向基类对象,又指向派生类对象(切片机制);而只有完成了虚函数重写,派生类与基类才具备不同的函数行为,达到“指向谁就调用谁”的效果。在类成员函数的前面加上virtual关键字修饰,该成员函数就成为了虚函数。public:// 成员函数前加 virtual 修饰,声明为虚函数。

Fuyo_0209的博客 433

C++ 求抛物线的顶点、焦点和准线(Finding the vertex, focus and directrix of a parabola)

已知抛物线方程 $ y = ax^2 + bx + c $,可利用公式求其顶点、焦点和准线。顶点为 $ \left( -\frac{b}{2a}, \frac{4ac - b^2}{4a} \right) $,焦点为 $ \left( -\frac{b}{2a}, \frac{4ac - b^2 + 1}{4a} \right) $,准线方程为 $ y = c - \frac{b^2 + 1}{4a} $。以 $ a=5, b=3, c=2 $ 为例,计算得顶点 $(-0.3, 1.55)$,焦点 $(-0

csdn_aspnet的专栏,请点击博客主页右上角三个点中的私信联系 163

c++ 超级大坑未定义行为如何避免(下)

尤其是通过。

拒绝AI文,努力在AI时代写出感动人心的好文章 141

华为校招笔试真题 10.28 - 稀疏整数求和(C++/Py/Java /Js/Go)

华为非AI笔试 10月28号真题,华为笔试第二题【稀疏整数求和】,提供详细思路讲解以及Java、C++、Python、Go、JavaScript多语言代码实现。华为非AI笔试笔试真题【稀疏整数求和】多语言题解。

qq_45776114的博客 612

C 语言指针与输入输出

指针是 C 语言的魂,输入输出是和人交互的入口。这篇文章把"变量在内存里到底长什么样"讲透,接着用代码验证原码/反码/补码为什么非用补码不可,再拿进制转换、数组、printf/scanf 的格式符收尾。所有输出都是本机 gcc 真实跑出来的。

2401_89953644的博客 151

从“不照人”到“剂量达标”:扇叶遮挡式UV-C空气消毒的工程边界与可验证评估框架

人机共存”正逐渐成为家用 UV-C 空气消毒产品的核心卖点。其中一类常见结构,是把 254 nm 紫外线灯置于吊扇扇叶上方,以扇叶盘作为物理遮挡,同时借助风扇强制对流实现空气循环消毒。专利文献对该结构的意图描述相当清晰:消毒平台遮挡紫外线灯朝下发出的光,使光线射向天花板,从而“不会让紫外线辐射到位于吊扇灯下方的用户”。然而,“结构上不直射”与“工程上安全且有效”之间,仍隔着一段需要实测数据来填补的距离。

HANDY09200的博客 684

【C++】【STL】string_OJ题

本文总结了9道C++ STL中关于string的OJ题目,按操作类型分为三组:反转与就地修改(如917、541、151)、计数与查找(如387、125、58)、模拟与大数运算(如415、43、8)。核心技巧包括双指针收敛、字符分类处理、进位控制及边界值处理。所有题目使用同一套源码,在标准库std::string与自定义容器MyString间切换仅需宏定义,验证结果一致性。代码通过随机测试用例与Trace函数逐行比对,确保逻辑正确性,强调接口对齐的重要性。

这里是Cocatrice的博客,感谢大家一直以来的支持和鼓励,如果对C/C++方向感兴趣的话,可以通过主页的微信名片加Cocatrice的好友,也可以通过邮箱:Chen31980094@163.com 联系,一同加油! 4355

C 语言 malloc() 详解与实战 Demo

本文详解C语言malloc()函数,涵盖其语法、内存原理及实战应用。通过动态整数、数组、结构体与链表的Demo,演示如何安全申请、使用与释放内存,强调避免内存泄漏与空指针检查的重要性,帮助开发者掌握动态内存管理核心技能。

tangPHP的博客 361

【C++ 基础入门】类和对象(上):封装、对象模型与 this 指针

C++类与对象知识(面向过程和面向对象的初步认识,类的初步认识,类的访问限定符及封装,类的作用域、实例化,类的对象模型,this指针)

2601_96587588的博客 862
上一篇: 驱动开发硬核特训 · USB 摄像头专题:原理 + 实战深度解析(基于 linux-imx)
下一篇: 设计模式每日硬核训练 Day 12:装饰器模式(Decorator Pattern)完整讲解与实战应用
嵌入式孙老师
博客等级 码龄7年 5686粉丝 898原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值