C++ 开源协程库libco——原理及应用
滴滴平台技术部·王亮2016 年11 月26 日
1 导论
使用C++ 来编写高性能的网络服务器程序,从来都不是件很容易的事情。在没有
应用任何网络框架,从epoll/kqueue 直接码起的时候尤其如此。即便使用libevent, libev
这样事件驱动的网络框架去构建你的服务,程序结构依然不会很简单。为何会这样?因
为这类框架提供的都是非阻塞式的、异步的编程接口,异步的编程方式,这需要思维方
式的转变。为什么golang 近几年能够大规模流行起来呢?因为简单。这方面最突出的
一点便是它的网络编程API ,完全同步阻塞式的接口。要并发?go 出一个协程就好了。
相信对于很多人来说,最开始接触这种编程方式,是有点困惑的。程序中到处都是同步
阻塞式的调用,这程序性能能好吗?答案是,好,而且非常好。那么golang 是如何做
到的呢?秘诀就在它这个协程机制里。
在go 语言的API 里,你找不到像epoll/kqueue 之类的I/O 多路复用(I/Omultiplexing)
接口,那它是怎么做到轻松支持数万乃至十多万高并发的网络IO 的呢?在Linux 或
其他类Unix 系统里,支持I/O 多路复用事件通知的系统调用(System Call)不外乎
epoll/kqueue,它难道可以离开这些系统接口另起炉灶?这个自然是不可能的。聪明的
读者,应该大致想到了这背后是怎么个原理了。
语言内置的协程并发模式,同步阻塞式的IO 接口,使得golang 网络编程十分容
易。那么C++ 可不可以做到这样呢?
本文要介绍的开源协程库libco,就是这样神奇的一个开源库,让你的高性能网络
服务器编程不再困难。
Libco 是微信后台大规模使用的C++ 协程库,在2013 年的时候作为腾讯六大开
源项目首次开源。据说2013 年至今稳定运行在微信后台的数万台机器上。从本届
ArchSummit 北京峰会来自腾讯内部的分享经验来看,它在腾讯内部使用确实是比较广
泛的。同go 语言一样,libco 也是提供了同步风格编程模式,同时还能保证系统的高并
发能力。
1
2 准备知识
2.1 协程(Coroutine)是什么?
协程这个概念,最近这几年可是相当地流行了。尤其go 语言问世之后,内置的协
程特性,完全屏蔽了操作系统线程的复杂细节;甚至使go 开发者“只知有协程,不知
有线程”了。当然C++,Java 也不甘落后,如果你有关注过C++ 语言的最新动态,可能
也会注意到近几年不断有人在给C++ 标准委员会提协程的支持方案;Java 也同样有一
些试验性的解决方案在提出来。
在go 语言大行其道的今天,没听说过协程这个词的程序员应该很少了,甚至直接接
触过协程编程的(golang,lua,python 等)也不在少数。你可能以为这是个比较新的东西,
但其实协程这个概念在计算机领域已经相当地古老了。早在七十年代,DonaldKnuth 在
他的神作TheArtofComputerProgramming 中将Coroutine 的提出者归于ConwayMelvin。
同时,Knuth 还提到,coroutines 不过是一种特殊的subroutines (Subroutine 即过程调用,
在很多高级语言中也叫函数,为了方便起见,下文我们将它称为“函数”)。当调用一
个函数时,程序从函数的头部开始执行,当函数退出时,这个函数的声明周期也就结
束了。一个函数在它的生命周期中,只可能返回一次。而协程则不同,协程在执行过程
中,可以调用别的协程自己则中途退出执行,之后又从调用别的协程的地方恢复执行。
这有点像操作系统的线程,执行过程中可能被挂起,让位于别的线程执行,稍后又从
挂起的地方恢复执行。在这个过程中,协程与协程之间实际上不是普通“调用者与被调
者”的关系,他们之间的关系是对称的(symmetric)。实际上,协程不一定都是这种对
称的关系,还存在着一种非对称的协程模式(asymmetric coroutines)。非对称协程其实
也比较常见,本文要介绍的libco 其实就是一种非对称协程,Boost C++ 库也提供了非
对称协程。
具体来讲,非对称协程(asymmetriccoroutines)是跟一个特定的调用者绑定的,协
程让出CPU 时,只能让回给原调用者。那到底是什么东西“不对称”呢?其