协程原理与设计

本文探讨了常见的线程模型,包括IO/Handle分离和合并的优缺点,并介绍了N:1协程的实施方案。重点讲解了M:N协程的设计,如Flare/fiber的特性,如工作窃取机制、多调度组以及与线程的互操作性。文章还讨论了性能测试和在不同场景下的适用性,强调了在高并发场景中,如何通过协程设计达到性能和易用性的平衡。
摘要由CSDN通过智能技术生成
什么是M:N协程?为什么要支持M:N协程?如何设计M:N协程?tRPC-Cpp引入了公司开源组件Flare/fiber作为底层库,本文多角度分析梳理了M:N协程的关键原理和特性。

1 常见线程模型的问题

在高并发编程场景,如互联网后台类业务中,既要保证高性能,也要保持编程的便利性。目前trpc-cpp中支持了2类模型:线程模型、N:1协程。

线程是操作系统的最小调度单元,可以通过系统调用挂起,也可能被操作系统随时抢占打断。以下是常见的各种线程并发模型。

为了支持高并发,多路复用(Window/IOCP,Linux/epoll等)是必须的手段。线程数量过多,会极大地降低系统性能。为此,trpc-cpp中实现了其中2种并发模型,都采取了“有限线程”+“多路复用”的方案。

1.1. IO/Handle分离:方案11

这也是很常见的线程模型,在Thrift、Tars等RPC框架中都有实现。关键特征即在于IO、Handle线程独立,通过队列通信。

  • 优点:
    • 各个Handle线程很自然地达到负载均衡。
    • 各个任务天然隔离,适应任意类型的业务:IO-Bound、CPU-Bound等。

  • 缺点:
    • 一个请求/回复,至少经过2个线程,会引入额外的调度延迟,在低延迟业务中有所不足。IO/Handle之间的通知机制需要良好设计,避免过多唤醒的系统调用。
    • 在高并发时,IO/Handle之间的全局队列需要良好的设计,否则可能成为系统瓶颈。
    • 由于Handle中提供了Promise/Future风格接口,此处需要额外注意continuation的运行线程控制,另外也需要考虑到CPU-Bound或者阻塞逻辑对continuation的影响,否则业务编程困难。
    • 在部分业务场景,合适的IO/Handle线程数量比例难以估算,配置困难。

1.2. IO/Handle合并:方案9

这种线程模型,和传统的高性能框架比较相似,如Nginx、Seastar等,关键特征在于各个工作线程独立运行,消除竞争,追求极致效率。

  • 优点:
    • 架构简洁可靠:全异步方案,逻辑统一,易于理解。
    • 对于NUMA架构天然适配。
    • 一个请求/回复,只在一个线程处理,对于低延迟业务友好。
    • 可以做到无锁编程。

  • 缺点:
    • 不允许阻塞代码,否则可能引起问题,所以对于业务开发门槛稍高。对于CPU-Bound业务同样不合适。
    • 同一个线程中的各个任务容易互相干扰。

1.3. N:1协程

N:1协程也即在多个协程协作运行在一个系统线程上,当然系统线程可以有多个。基于封装层次,分为:非hook、部分hook、全面hook。 目前常见的C++协程实现都是这种,如libco,spp协程等。

trpc-cpp在上述IO/Handle分离线程模型的Handle线程中支持N:1协程的子模式。协程库实现来自spp框架,是部分hook,也即网络相关的系统调用。默认是没有开启的,需要用户主动调用spp提供的网络接口。

这个方案很大的优点是,可以完全无锁编写同步风格代码,对于普通的互联网后台业务(IO-Bound型)编程是很友好的。

但是也有一个明显的缺点࿰

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值