我感觉我的软件延迟很高
我现在觉得原因是:
(1)使用3个线程来接收3个支路的信息,使用emit 发送数据
(这已经使用了Qt的队列(跨线程))
(2)在接收端又使用了3个队列来管理3个支路,然后转发并处理。(又使用了队列)
一、先把你的数据链路画出来
根据你的描述,我推断链路是这样的:
cpp
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ 接收线程1 │ │ 接收线程2 │ │ 接收线程3 │
│ recv(sock1) │ │ recv(sock2) │ │ recv(sock3) │
│ │ │ │ │ │ │ │ │
│ emit ① │ │ emit ① │ │ emit ① │
└────┼────────┘ └────┼────────┘ └────┼────────┘
│ │ │
▼ ▼ ▼
┌──────────────────────────────────────────────┐
│ Qt 事件队列(跨线程,第 1 次排队) │
└──────────────────────────────────────────────┘
│ │ │
▼ ▼ ▼
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ 队列1 │ │ 队列2 │ │ 队列3 │
│(第 2 次排队)│ │(第 2 次排队)│ │(第 2 次排队)│
└──────┬──────┘ └──────┬──────┘ └──────┬──────┘
│ │ │
▼ ▼ ▼
┌──────────────────────────────────────────────┐
│ 处理线程 / 转发逻辑 │
│ 从 3 个队列取 → 处理 → sendto │
└──────────────────────────────────────────────┘
每一段都是一个"生产者-消费者"队列,每个队列都可能积压。
二、逐段量化延迟
假设你的包速率是 1 万包/秒(每包 0.1ms 到达间隔)。
第 1 次排队:emit → Qt 事件队列
emit sign_bdp(bdp) 跨线程投递,实际上是:
cpp
接收线程:构造 QMetaCallEvent → 加锁 → 放入目标线程事件队列 → 写 eventfd 唤醒
目标线程:事件循环被唤醒 → 取事件 → 加锁 → 调用槽函数
单次 emit 的延迟:
-
加锁 + 入队 + 唤醒:约 5~20 微秒
-
如果目标线程正忙:排队等待 = 队列长度 × 单次处理时间
队列积压时的延迟:
| 队列长度 | 目标线程单次处理 | 新包等待时间 |
|---|---|---|
| 100 | 10 微秒 | 1 毫秒 |
| 1000 | 10 微秒 | 10 毫秒 |
| 10000 | 10 微秒 | 100 毫秒 |
| 100000 | 10 微秒 | 1 秒 |
这就是第一层延迟。如果目标线程处理慢(比如 GUI 线程还要画界面),队列会持续增长,延迟线性上升。
第 2 次排队:从 Qt 队列取出 → 放入自己的队列
如果接收槽函数只是把数据再塞进自己的队列,那么:
cpp
槽函数被调用 → 加锁 → 放入队列1 → 解锁 → 返回
这一段本身很快 (几微秒),但它不解决问题 ,只是把延迟转移到下一个环节。
第 3 段:处理线程从队列取 → 处理 → 转发
cpp
处理线程:从队列1取 → 处理 → sendto
正确的做法 :减少中间环节,让数据尽快到达处理逻辑
方案 1:合并两段队列(收益最大)
去掉中间的 emit 或去掉中间的队列,只保留一次排队。
做法 A:接收线程直接处理 + 转发,不 emit
做法 B:3 个接收线程 + 1 个处理线程,但只用一次队列
cpp
接收线程1 ─┐
接收线程2 ─┼──► 一个共享队列 ──► 处理线程
接收线程3 ─┘
接收线程收到数据后,直接放入处理队列 (不用 emit,用 QMutex + QQueue 或无锁队列),处理线程从队列取。
好处:
-
只有一次排队
-
接收线程只做
recvfrom+ 入队,极快 -
处理线程专注处理
代价:
-
需要自己实现队列(
QMutex+QWaitCondition或QQueue+std::mutex) -
不如
emit优雅,但快得多
方案 2:一个线程管 3 条支路(如果速率不高)
如果 3 条总速率 < 5 万包/秒,一个线程 + QSocketNotifier 是最优:
cpp
一个线程:
QSocketNotifier × 3 → onReadable(idx) → 处理 → 转发
零跨线程投递,零队列,延迟最低。
前提:处理逻辑不阻塞、不碰 GUI。
结合你的场景(3 条 UDP 支路,互相转发,Qt):
推荐架构
cpp
┌──────────────────────────────────────────────┐
│ 一个工作线程 │
│ │
│ QSocketNotifier × 3 │
│ sock1 ──┐ │
│ sock2 ──┼──► onReadable(idx) │
│ sock3 ──┘ │ │
│ ▼ │
│ 处理 + 转发给另外两条 │
│ │ │
│ ▼ │
│ 批量 emit(给 GUI,20ms 一次) │
└──────────────────────────────────────────────┘
理由:
-
一个线程 → 零跨线程队列,零
emit延迟 -
QSocketNotifier→ 无数据不占 CPU -
处理 + 转发在同一个线程 → 共享 socket 无需加锁
-
只对 GUI 显示 用批量
emit,且降频到 50 次/秒