






























优点:
云架构中常常是更大架构层面的EDA, Event-driven architecture事件驱动架构,是一种跨服务跨系统的分布式现代架构模式,基于发布-订阅的消息异步通讯架构。
Notes: EDA上架构层面的概念,与设计模式的EventLoop并不在同一层概念,但本质上都是事件驱动思想的衍生。
https://bytebytego.com/guides/how-do-message-queue-architectures-evolve/


局部图:

Event -> EventLoop with Queue -> HandlerA,HandlerB,HanlderC...
对于小型程序来说,实现EDA的一种方式就是以EventLoop事件循环为中心,通过发布订阅的方式来实现微型EDA。
最小的EDA需要 EventPublisher-> Event ->EventLoop->EventHandlers
组成部分:
classDiagram class EventQueue { Event buffer[256] int head int tail int size pthread_mutex_t mutex pthread_cond_t not_empty pthread_cond_t not_full } class Event { EventType type int param1 int param2 void* data } class HandlerTable { EventHandler handlers[EVENT_TYPE_COUNT] +register_handler(type, fn) +handle_event(e) } EventQueue o-- Event : "buffer 存储" HandlerTable ..> Event : "按 type 分发" style EventQueue fill:#2d6a4f,color:#fff,stroke:#1b4332 style Event fill:#264653,color:#fff,stroke:#1d3557 style HandlerTable fill:#e76f51,color:#fff,stroke:#c1440e
sequenceDiagram box rgb(42,84,122) 生产者 participant P as 生产者线程 end box rgb(231,111,81) 同步原语 participant M as mutex participant Q as queue participant NE as cond not_empty participant NF as cond not_full end box rgb(45,106,79) 消费者 participant C as event_loop_thread end Note over C: 🟢 while(running) 循环开始 C->>M: 🔒 pthread_mutex_lock() Note over C: size==0, 队列空 C->>NE: 😴 pthread_cond_wait(not_empty, mutex) Note over C: ⚠️ 原子地释放 mutex 并挂起 P->>M: 🔒 pthread_mutex_lock() Note over P: 获得锁 P->>Q: 📝 buffer[tail] = event<br/>tail = (tail+1) % MAX<br/>size++ P->>NE: 📢 pthread_cond_broadcast(not_empty) Note over NE: 唤醒所有等待者 P->>M: 🔓 pthread_mutex_unlock() Note over C: ⏰ 被唤醒, 自动重新获得 mutex Note over C: 🔄 while(size==0) 再检查<br/>防 spurious wakeup C->>Q: 📤 event = buffer[head]<br/>head = (head+1) % MAX<br/>size-- C->>NF: 📢 pthread_cond_signal(not_full) C->>M: 🔓 pthread_mutex_unlock() Note over C: 🎯 handle_event → handlers[type](e) Note over C: 🔁 回到 while 顶部
sequenceDiagram box rgb(42,84,122) 生产者 participant P as 生产者线程 end box rgb(231,111,81) 同步原语 participant M as mutex participant Q as queue participant NF as cond not_full end box rgb(45,106,79) 消费者 participant C as event_loop_thread end P->>M: 🔒 lock() Note over P: size==MAX, 队列满 P->>NF: 😴 wait(not_full, mutex) Note over P: ⚠️ 释放 mutex 并挂起 C->>M: 🔒 lock() C->>Q: 📤 pop 事件, size-- C->>NF: 📢 signal(not_full) C->>M: 🔓 unlock() Note over P: ⏰ 被唤醒, 重获 mutex Note over P: 🔄 while(size==MAX) 再检查 P->>Q: 📝 push 事件, size++ P->>M: 🔓 unlock()
flowchart TD A["queue_pop(q, &e)"] A1["🔒 lock(mutex)"] A2["😴 while(size==0): wait(not_empty)"] A3["📤 取出 buffer[head], size--"] A4["📢 signal(not_full)"] A5["🔓 unlock(mutex)"] B{e.type == QUIT?} C["📢 queue_push(q, &e)\n转发 QUIT"] D["🛑 break"] E["🎯 handle_event(&e)"] E1["查 handlers[e.type]"] E2["调用回调函数"] A --> A1 --> A2 --> A3 --> A4 --> A5 A5 --> B B -- Yes --> C --> D B -- No --> E --> E1 --> E2 --> A style A fill:#264653,color:#fff,stroke:#1d3557 style A1 fill:#e76f51,color:#fff,stroke:#c1440e style A2 fill:#e9c46a,color:#000,stroke:#f4a261 style A3 fill:#2a9d8f,color:#fff,stroke:#21867a style A4 fill:#e76f51,color:#fff,stroke:#c1440e style A5 fill:#e76f51,color:#fff,stroke:#c1440e style B fill:#f4a261,color:#000,stroke:#e76f51 style C fill:#264653,color:#fff,stroke:#1d3557 style D fill:#c1121f,color:#fff,stroke:#780000 style E fill:#2d6a4f,color:#fff,stroke:#1b4332 style E1 fill:#2d6a4f,color:#fff,stroke:#1b4332 style E2 fill:#2d6a4f,color:#fff,stroke:#1b4332
| 颜色 | 含义 |
|---|---|
| 🟧 橙/红 | mutex 操作 (lock / unlock / signal) |
| 🟨 黄 | 条件等待 (cond_wait) |
| 🟩 绿 | 事件分发 (handle_event / 回调) |
| 🟦 蓝 | 队列数据操作 (push / pop) |
while (running) {
queue_pop(q, &e);
if (e.type == EVENT_QUIT) { ... break; }
- handle_event(&e);
+ thread_pool_submit(&pool, &e); // 异步派发,不阻塞
}
+ thread_pool_destroy(&pool); // loop 退出后等待 worker 完成
这个 event loop 的核心就是一个基于 mutex + 条件变量的有锁阻塞队列。
本质上整个模型可以归结为:
loop 线程阻塞在 queue_pop (锁 + cond_wait)
→ 有事件时被唤醒 (锁 + cond_signal/broadcast)
→ 取出事件 → 派发
→ 回去继续阻塞
| 问题 | 解法 |
|---|---|
| loop 空闲时如何不占 CPU | wait(not_empty) 挂起线程,由 push 端 broadcast 唤醒 |
| 队列满时生产者怎么办 | wait(not_full) 挂起,由 pop 端 signal 唤醒 |
为什么用 while 不用 if |
pthread 允许 spurious wakeup,醒来后必须重新检查条件 |
wait 时 mutex 怎么办 |
cond_wait 原子地释放 mutex 并挂起,被唤醒时自动重新加锁 |
| 为什么 not_empty 用 broadcast | 多消费者需唤醒所有等待者 |
| 为什么 not_full 用 signal | pop 只腾出一个位置,唤醒一个生产者即可 |
| (改进) 这里是单线程eventloop+单线程handler会堵塞loop,如何做多线程 | 新增线程池,然后将handle_event改为提交到线程池执行即可达到不堵塞loop的需求 |
| (改进) 用无锁队列实现呢 | 推荐eventfd+环形队列,利用内核的eventfd read堵塞完成调度 |
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。