微信开源C++Libco介绍与应用(一)

本文介绍了微信开源的C++协程库Libco,它提供同步风格编程,支持高并发。Libco是腾讯大规模使用的协程库,其非对称协程设计简化了高性能网络服务器编程。文章通过对比Golang的协程,解释了Libco的工作原理,包括协程的概念、Libco的使用示例和关键数据结构。
摘要由CSDN通过智能技术生成

导论

使用 C++ 来编写高性能的网络服务器程序,从来都不是一件很容易的事情。

在没有应用任何网络框架的情况下,从 epoll/kqueue 直接码起的时候更是如此。即便使用 libevent、 libev 这样事件驱动的网络框架去构建你的服务,程序结构依然不会很简单。

为何会这样?因为这类框架提供的都是非阻塞式的、异步的编程接口,异步的编程方式,这需要思维方式的转变。

为什么 Golang 近几年能够大规模流行起来呢?因为简单。这方面最突出的一点便是它的网络编程 API,完全同步阻塞式的接口。要并发?Go出一个协程就好了。

相信对于很多人来说,最开始接触这种编程方式,是有点困惑的。程序中到处都是同步阻塞式的调用,这程序性能能好吗?答案是好,而且非常好。那么 Golang 是如何做到的呢?秘诀就在它这个协程机制里。

在 Go 语言的 API 里,你找不到像 epoll/kqueue 之类的 I/O 多路复用(I/O multiplexing)接口,那它是怎么做到轻松支持数万乃至十多万高并发的网络 IO 呢?

在 Linux 或其他类 Unix 系统里,支持 I/O 多路复用事件通知的系统调用(System Call)不外乎 epoll/kqueue,它难道可以离开这些系统接口另起炉灶?这个自然是不可能的。聪明的读者,应该大致想到了这背后是怎么个原理了。

语言内置的协程并发模式,同步阻塞式的 IO 接口,使得 Golang 网络编程十分容易。那么 C++ 可不可以做到这样呢?

本文要介绍开源协程库 libco,就是这样神奇的一个开源库,可以让你的高性能网络服务器编程不再困难。

Libco 是微信后台大规模使用的 C++ 协程库,在 2013 年的时候作为腾讯六大开源项目首次开源。据说 2013 年至今仍稳定地运行在微信后台的数万台机器上。从本届 ArchSummit 北京峰会来自腾讯内部的分享经验来看,它在腾讯内部使用确实是比较广泛的。

同 Go 语言一样,libco 也是提供了同步风格编程模式,同时还能保证系统的高并发能力。


准备知识

协程(Coroutine)是什么?

协程这个概念,最近这几年可是相当地流行了。尤其 Go 语言问世之后,内置的协程特性,完全屏蔽了操作系统线程的复杂细节;甚至使 Go 开发者“只知有协程,不知有线程”了。

当然 C++, Java 也不甘落后,如果你有关注过 C++ 语言的最新动态,可能也会注意到近几年不断有人在给 C++ 标准委员会提协程的支持方案;Java 也同样有一些试验性的解决方案在不断被提出来。

在 Go 语言大行其道的今天,没听说过协程这个词的程序员应该很少了,甚至直接接触过协程编程的(Golang、lua、python 等)也不在少数。

你可能以为这是一个比较新的东西,但其实协程这个概念在计算机领域已经相当地古老了。早在七十年代,Donald Knuth 在他的神作 The Art of Computer Programming 中将 Coroutine 的提出者归于 Conway Melvin。同时,Knuth 还提到,Coroutines 不过是一种特殊的Subroutines(Subroutine即过程调用,在很多高级语言中也叫函数,为了方便起见,下文我们将它称为“函数”)。

当调用一个函数时,程序从函数的头部开始执行,当函数退出时,这个函数的声明周期也就结束了。一个函数在它的生命周期中,只可能返回一次。

而协程则不同,协程在执行过程中,可以调用别的协程自己则中途退出执行,之后又从调用别的协程的地方恢复执行。这有点像操作系统的线程,执行过程中可能被挂起,让位于别的线程执行,稍后又从挂起的地方恢复执行。在这个过程中,协程与协程之间实际上不是普通“调用者与被调者”的关系,他们之间的关系是对称的(symmetric)。

实际上,协程不一定都是这种对称的关系,还存在着一种非对称的协程模式(asymmetric coroutines)。非对称协程其实也比较常见,本文要介绍的 libco 其实就是一种非对称协程,Boost C++ 库也提供了非对称协程。

具体来讲,非对称协程(asymmetric coroutines)是跟一个特定的调用者绑定的,协程让出 CPU 时,只能让回给原调用者。那到底是什么东西“不对称”呢?

其实,非对称在于程序控制流转移到被调协程时使用的是 call/resume 操作,而当被调协程让出 CPU 时使用的却是 return/yield 操作。此外,协程间的地位也不对等,caller 与 callee 关系是确定的,不可更改的,非对称协程只能返回最初调用它的协程。

对称协程(symmetric coroutines)则不一样,启动之后就跟启动之前的协程没有任何关系了。协程的切换操作,一般而言只有一个操作 — yield,用于将程序控制流转移给另外的协程。对称协程机制一般需要一个调度器的支持,按一定调度算法去选择 yield 的目标协程。

Go 语言提供的协程,其实就是典型的对称协程。除了对称,Goroutines 还可以在多个线程上迁移。这种协程跟操作系统中的线程非常相似,甚至可以叫做“用户级线程”了。

而 libco 提供的协程,虽然编程接口跟 pthread 有点类似,“类 pthread 的接口设计”、“如线程库一样轻松”,本质上却是一种非对称协程。这一点不要被表象蒙蔽了。

事实上,libco 内部还为保存协程的调用链留了一个 stack 结构,而这个 stack 大小只有固定的 128。使用 libco,如果不断地在一个协程运行过程中启动另一个协程,随着嵌套深度增加就可能会造成这个栈空间溢出。


Libco 使用简介

一个简单的例子

在多线程编程教程中,有一个经典的例子:生产者消费者问题。事实上,生产者消费者问题也是最适合协程的应用场景。那么我们就从这个简单的例子入手,来看一看使用 libco 编写的生产者消费者程序( 例程代码来自于libco源码包)。


生产者和消费者协程

struct 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值