跳到主要内容

线程模型选择

krpc 提供异步接口,但在多线程/多核环境下如何选择线程模型,需要结合 QPS、延迟、CPU 核心数 以及 任务特性 来判断。


1. 同步 vs 异步

  • 核心原则:低延迟场景,先用简单易懂的同步接口;只有同步无法满足性能时,才使用异步接口。

  • 异步的误区

  • JavaScript 风格的单线程异步回调在多线程环境中无法直接使用。

  • 转换阻塞同步代码到回调风格很难,尤其是:

  • 循环内部阻塞

  • 条件分支复杂

  • 依赖第三方库

  • 结果往往是“难以维护且性能不佳”。

  • krpc 异步不同于 JS 异步:

  • 回调运行在不同线程

  • 可阻塞回调(只要线程资源充足)

  • 支持组合通道(Combo Access)简化复杂调用


选择规则:同步还是异步?

公式:QPS * latency (in seconds)

  • 如果这个值 ≈ CPU 核心数 → 使用同步
  • 如果这个值 >> CPU 核心数 → 使用异步

示例:

QPS延迟平均并发请求推荐
200010ms20同步
10050ms500异步
500100ms50同步可行

解释

  • 值远大于 CPU 核心数 → 大量线程阻塞 → 异步可节省线程/内存
  • 值接近或小于 CPU 核心数 → 异步收益不大,保持同步代码简单可读

2. 异步 vs kthread

  • 只需并发 RPC 调用 → 异步比 kthread 更高效

  • kthread 创建和阻塞 RPC 会增加额外开销

  • 推荐用异步 + ParallelChannel 组合调用

  • 需要多核并行计算 → kthread 合理

  • 可构建树形并行计算

  • 举例:三阶段可并行处理

kthread th1, th2;
kthread_start_background(&th1, NULL, part1, args1);
kthread_start_background(&th2, NULL, part2, args2);
part3(args3); // 当前线程处理最慢任务
kthread_join(th1);
kthread_join(th2);
  • 经验要点
  1. kthread 创建到执行延迟 ≈ 3–30µs
  2. 只有任务耗时 > 1ms,使用 kthread 才显著
  3. 尽量在当前线程执行最慢的任务,减少调度延迟影响
  4. 可用 ExecutionQueue 管理顺序执行任务(内置 kthread)

3. 小结:线程模型选择原则

场景推荐方案说明
低延迟、低 QPS同步接口简单易读,线程资源足够
高并发阻塞 RPC异步接口节省线程栈和资源
CPU 密集型、多核并行kthread支持树形任务并行,充分利用 CPU
任务需要顺序执行ExecutionQueue任务顺序严格,利用 kthread 简化同步

建议流程

  1. 先同步:简单、低延迟场景
  2. 遇到瓶颈 → 异步接口
  3. 需要并行计算 → kthread 或 ExecutionQueue
  4. 高密度依赖任务 → 考虑 Taskflow(复杂 DAG 并行)

简言之:同步优先,异步节约线程,kthread 用于 CPU 密集或并行计算,ExecutionQueue 用于保证任务顺序