并发模型选型:多线程 vs 协程 vs Actor模型
做高频交易系统这些年,我一直在跟并发模型打交道。说白了,选错模型,后面所有优化都是白搭。今天咱们就聊聊这三种主流模型,在交易场景下到底该怎么选。
一、多线程模型:老牌选手,依然能打
多线程,大家最熟悉了。Java里new一个Thread,C++里std::thread,上手快。但交易系统里用多线程,坑不少。
核心优势:
- CPU利用率高,多核并行计算
- 共享内存,数据交换零拷贝
- 操作系统原生支持,调度成熟
我在项目中遇到过一个问题:订单处理线程和风控线程共享一个订单簿。加锁吧,性能掉得厉害;不加锁吧,数据就乱了。后来用了无锁队列才勉强解决。
避坑指南:我曾经在一个项目里用了20个线程处理行情数据,结果锁竞争导致CPU大部分时间都在空转。后来发现,线程数超过CPU核心数两倍后,性能反而下降。
多线程适合什么场景?我个人习惯用在计算密集型的任务上,比如策略计算、风险指标计算。但要注意,线程间通信越少越好。
二、协程模型:轻量级选手,IO密集型利器
协程这几年火得不行。Go的goroutine,Python的asyncio,C++20的coroutine。说白了,协程就是用户态线程,切换成本极低。
核心优势:
- 上下文切换开销小,纳秒级
- 内存占用低,百万协程不是梦
- 代码写起来像同步,逻辑清晰
你想想看,交易系统里大量IO操作:网络收发、磁盘写入。如果用多线程,每个线程都在等IO,白白浪费CPU。协程就不一样了,遇到IO自动让出CPU,等数据来了再继续执行。
我的经验:在对接交易所行情网关时,我用协程处理WebSocket连接。一个进程轻松管理上千个连接,每个连接一个协程,代码写起来跟同步一样简单。
但协程也有短板。计算密集型任务里,协程反而可能拖后腿。为什么?因为协程调度器本身也有开销,而且协程之间没法真正并行(除非配合多线程)。
三、Actor模型:分布式场景的救星
Actor模型,Erlang/Elixir的看家本领,Akka在JVM上发扬光大。每个Actor是一个独立实体,通过消息通信,不共享任何状态。
核心优势:
- 天然隔离,没有共享状态,不用加锁
- 容错性强,单个Actor挂了不影响全局
- 分布式友好,Actor可以跨节点通信
我记得有个项目,需要做跨交易所的套利。多个交易所的订单管理、风控、资金管理,如果用多线程,光锁就够你喝一壶的。用Actor模型,每个交易所一个Actor,消息驱动,代码清晰多了。
注意:Actor模型不适合需要强一致性的场景。比如订单簿的撮合,多个Actor同时修改订单簿,消息顺序和一致性很难保证。我曾经试过用Actor做撮合引擎,结果性能还不如单线程。
四、三种模型对比:一张表说清楚
| 维度 | 多线程 | 协程 | Actor模型 |
|---|---|---|---|
| 上下文切换开销 | 微秒级(内核态) | 纳秒级(用户态) | 取决于实现 |
| 内存占用 | 高(MB级/线程) | 低(KB级/协程) | 中等 |
| 数据共享 | 共享内存(需加锁) | 共享内存(需加锁) | 消息传递(无共享) |
| 适合场景 | 计算密集型 | IO密集型 | 分布式、容错 |
| 交易系统适用性 | 策略计算、风控 | 行情接收、网络通信 | 跨交易所套利 |
五、交易场景下的选型建议
说了这么多,到底怎么选?我个人的经验是:
- 核心撮合引擎:单线程或者多线程(严格控制线程数)。撮合逻辑对一致性要求极高,多线程反而容易出问题。
- 行情处理:协程优先。行情数据量大、IO密集,协程的轻量级优势明显。
- 策略计算:多线程。计算密集,需要充分利用多核CPU。
- 跨系统通信:Actor模型。多个子系统解耦,容错性好。
一个小技巧:实际项目中,我经常混合使用。比如用多线程做计算,用协程做网络IO,用Actor做模块间通信。没有银弹,只有最适合的组合。
六、核心逻辑图:并发模型选型决策树
七、总结
嗯,说了这么多,其实就一句话:没有最好的模型,只有最合适的模型。多线程、协程、Actor模型各有千秋,关键看你的业务场景。
我个人习惯是:先分析瓶颈在哪里。是CPU不够?IO太慢?还是分布式通信复杂?然后对症下药。别一上来就选最时髦的,也别死守老一套。
最后提醒:不管选哪种模型,一定要做性能压测。我曾经在测试环境跑得好好的,一上生产就崩。后来发现是锁竞争在低负载下不明显,高负载下直接爆炸。所以,压测!压测!再压测!
公众号:蓝海资料掘金营,微信deep3321