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

📘 本篇目标是:用通俗易懂的方式,从零构建一个支持线程池调度的事件中心 SignalHub,结合我们前几天学到的协程与 resume 技术,打造一个结构清晰、易于学习、可用于实际工程的异步事件系统


✅ 简单回顾:我们已经学会了什么?

技术点说明
协程机制使用 co_await 挂起,resume 恢复执行
Awaiter 类用于管理协程何时挂起、谁来恢复
信号发射使用 emit() 来触发一个事件
调度器接口使用 Dispatcher 控制在哪个线程运行

📌 今天的重点是:支持线程池的多事件中心设计,并结合协程 resume。


在这里插入图片描述

✅ 一、创建一个简单线程池

class ThreadPool : public Dispatcher {
public:
    ThreadPool(size_t count = 2) : 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;
};

📌 用法:用 dispatch(fn) 提交任务给线程池。


✅ 二、定义 Awaiter 类型(协程等待信号)

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(); });
    }
};

📌 说明:这个类负责让协程挂起,并在合适的时候 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;
};

📌 用法:

  • wait("login"):协程等待某个事件 key
  • emit("login", "user"):发出事件并 resume 对应协程

✅ 四、实战案例:等待登录成功

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); // 初始化线程池(2 个线程)
    AwaitableSignalHub<std::string, std::string> hub(&pool); // 构建事件中心

    auto task = waitLogin(hub); // 协程开始等待登录事件

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

    std::this_thread::sleep_for(std::chrono::seconds(1));
    pool.stop(); // 停止线程池
    return 0;
}

📌 输出:

✅ 登录成功,用户是:Alice

✅ 今日重点总结

关键组件作用
ThreadPool处理异步任务的线程池
Dispatcher 接口控制协程 resume 到哪个线程
Awaiter/HubAwaiter协程挂起点,封装 resume 动作
AwaitableSignalHub事件中心,支持事件挂起 + resume

🧠 巩固小问答

Q1:协程 resume 到哪个线程?

由 Dispatcher 决定,可能是线程池线程,也可能是主线程。

Q2:事件 key 可以是什么?

任意类型(如字符串、枚举、int 等)都可以作为事件标识。

Q3:多个协程可以同时监听同一个事件吗?

可以,多个协程会各自 resume。

Q4:wait 之后不 emit 会怎样?

协程会一直挂起,直到事件被 emit。

Q5:是否适合 UI 事件驱动?

非常适合。配合主线程 Dispatcher 可实现按钮点击、输入框更新等响应。


🔭 下一步 Day 16:结合 GUI 框架构建响应式界面组件

  • 如何将 SignalHub 嵌入到图形界面(如 ImGui、SDL)
  • 如何监听按钮点击、输入更新等 GUI 事件并响应协程
  • 构建轻量响应式事件绑定机制,支持跨平台主线程 resume

📌 如果你更偏向某个实际应用方向(游戏引擎、嵌入式控制、UI 表单等),可以告诉我,我会根据你的需求来定制 👨‍💻

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值