C++ Coroutine

作用、原理和应用

作用

协程的作用是在同一个线程中通过保存代码执行段状态,进行代码段的分次执行,以及多个代码段的交织执行。每个代码段可以成为一个协程。

本质上讲Coroutine是一种特殊类型的subroutine,它可以在执行过程中多次暂停(yield)、然后过一段时间又可以重新从暂停的地方开始执行(resume),在重新执行时subroutine的上下文(局部变量)是保持的。function object也可以有类似的保存上下文的能力,coroutine不同的地方在于每次是从上次暂停的地方开始继续执行,而function object每次是从头开始执行。《微信服务器协程原理解析及优化

协程是在线程之上由“用户”构建的并发单元,对OS来说无感知,协程的切换由用户自己管理和调度。(这里的用户是相较于内核而言的,一些通用库这里也理解为用户)《谈谈对协程的理解

协程的好处是避免了线程之间的频换切换、以及线程安全的优化(对于同一个变量的访问无需做同步控制)。
协程极大的优化了程序员的编程体验,同步编程风格能快速构建模块,并易于复用,而且有异步的性能(这个看具体库的实现),也不用陷入callback hell的深坑。

协程的好处有哪些vczh的回复,把C#的async/await与协程的概念关联起来,厉害。

协程到底有没有性能提升?

历史上现有协程,再有线程。OS利用协程来模拟多任务并发,但由于是非抢占式的,导致多任务无法公平共享,所以被抢占式的线程替代。说协程性能好的,其实真正的原因是因为瓶颈在IO上面,而这个时候真正发挥不了线程的作用。《协程的好处有哪些?

原理

原理是将协程所用的堆栈信息保存起来,下一次使用时再装载,以时间换空间。tishion说,试想操作系统中有500个任务,而系统只有固定的255个tss段,怎么办?分时复用,当前被调度的任务可以获得一个TSS段,然后把自己的TSS内容拷贝到那个TSS段,等到这个任务的时间用完,就把自己的TSS拷贝到一边保存起来。下一个被调度的再拷贝进来。就是一个碗10个人用来吃饭。就是一个碗10个人用来吃饭。Windows和Linux的任务调度都是这个思想和实现。「共享栈」也可以叫作 Copy Stack ,意思是栈空间是所有的协程共享的,在切换的时候通过把协程栈的内容copy-in/copy-out来实现栈的切换。

协程的实现需要wrapper around native APIs for coroutines like fibers on Windows and ucontext.h on POSIX systems, falling back to a setjmp/longjmp-based implementation on other platforms. Improving a large C++ project with coroutines

应用

There’s a lot of code that can be simplified using coroutines, Improving a large C++ project with coroutines

微信的后台也是利用协程进行了优化和代码简化。

现有的Coroutine libraries

boost.coroutine

boost.asio对boost.coroutine库有个封装,

boost.coroutine是stackfull的。stackless的coroutine不能在reenter中使用局部变量,即使加个大括号可以使用局部变量,再次进入函数的时候变量的值也会丢失,而stackfull的则不会。这就带来了很大的方便。
yyzybb537/libgo(Go-style concurrency in C++11)也实现了一个coroutine,作者有详细的性能测试对比,而且说:

1.asio的coroutine会被网络IO阻塞,虽然使用者可以使用异步网络io,但是第三方库并不会,因此现有的使用同步网络IO的第三方库统统不能用。
2.asio的coroutine只是简单的网络层面的协程,缺失很多feature,比如channel mutex等,仅仅是个协程模型的网络库. 这个corotuine却是一个完整的协程模型的框架,特性完整.

libgo coroutine

libgo是魅族公司内部的一个项目。libgo是一个使用C++11编写的协作式调度的stackful协程库,同时也是一个强大的并行编程库, 是专为Linux服务端程序开发设计的底层框架,不过目前也支持Windows。libgo依赖于boost。
知乎上如何评价c++的协程库libgo?中作者的回答
作为libgo的作者,好坏不便于评价,至少我们项目组用的都挺爽的~在此展示一些设计理念和测试结果吧.
Q: 更重要的是hook了阻塞的api,使得可以与第三方的同步网络库无缝兼容,不知道有没有坑?A: libgo在hook的时候,会让api的行为与系统原生api完全保持一致,每一个边界条件的返回值和错误码都是一致的。在魅族推送系统线上的100多台服务器中经过了半年左右的考验,目前最新的2.6版本已经非常稳定了。
Q: 见过不少协程的轮子,但从没见过支持多线程调度的,我觉得这个特性很有竞争力,就是不知道调度性能如何?A: 多线程调度使用的是worksteal算法,性能上的损耗很小,远远快于golang。
以下是libgo协程切换速度和管道读写速度与golang对比测试的结果:------------- libgo ---------------BenchmarkSwitch_1 1000000 154 ns/opBenchmarkSwitch_1000 1000000 105 ns/opBenchmarkSwitch_10000 1000000 553 ns/opBenchmarkChannel_0 1000000 485 ns/opBenchmarkChannel_1 1000000 299 ns/opBenchmarkChannel_10000 10000 108 ns/opBenchmarkChannel_5000000 5000000 113 ns/op--------------------------------------------------- golang --------------BenchmarkSwitch_1 1000000 1438 ns/opBenchmarkSwitch_1000 1000000 1654 ns/opBenchmarkSwitch_10000 500000 2375 ns/opBenchmarkChannel_0 1000000 1536 ns/opBenchmarkChannel_1 500000 3037 ns/opBenchmarkChannel_N 50000000 65.0 ns/op--------------------------------------
测试代码在libgo/test/golang目录中,直接执行test.sh脚本即可得到结果.
下面有热心用户给出的另一组结果截然不同的测试数据,在此补充一点,libgo使用boost1.59以上的版本才可以获得这样的高性能,cmake命令执行参数:$ cmake .. -DENABLE_BOOST_CONTEXT=ON

另外, 很重要的一点. 在实际使用中, 我们基于libgo封装了一个网络库(libgonet)作为我们的base框架.libgonet也是个开源项目, 希望在linux系统上基于libgo开发网络程序的, 可以关注一下哦~
编辑于2016-10-21

state-threads

这是某些人认为很好的协程库。

The State Threads Library is a small application library which provides
a foundation for writing fast and highly scalable Internet applications
(such as web servers, proxy servers, mail transfer agents, and so on,
really any network-data-driven application) on UNIX-like platforms. It
combines the simplicity of the multithreaded programming paradigm, in
which one thread supports each simultaneous connection, with the
performance and scalability of an event-driven state machine
architecture. In other words, this library offers a threading API for
structuring an Internet application as a state machine. For more
details, please see the library documentation in the "docs" directory or
on-line at

http://state-threads.sourceforge.net/docs/

The State Threads Project is an open source project for maintaining and
enhancing the State Threads Library. For more information about this
project, please see

http://state-threads.sourceforge.net/

Coroutines in LLVM

LLVM的协程,还没来得及看。

zyzacz的cpp协程代码

Yuandong-Chen/coroutine

RethinkDB 核心所用的协程

libcoroutine
Libcoroutine is a thin, cross-platform wrapper around native APIs for coroutines like fibers on Windows and ucontext.h on POSIX systems, falling back to a setjmp/longjmp-based implementation on other platforms.

Improving a large C++ project with coroutines
中说:

At the core of RethinkDB is a highly parallel B-tree implementation. Due to our performance requirements, it is too expensive to create a native thread for each request. Instead, we create one thread per CPU on the server (logical CPU in the case of hyperthreading) and use cooperative concurrency within a thread.

A single thread will have multiple logically concurrent units of control, taking turns when a unit needs to block. Blocking needs to take place ultimately for either I/O–waiting for information from the network or disk, or waiting to be notified that sending information there has completed–or for coordination with other threads. On top of this, we implemented higher-level abstractions which also block.

具体实现和注意事项

注意事项:

由于协程的切换是由程序员自己调度的,所以很难说协程切换的代价比省去的线程切换代价小,合理的方式应该是通过测试工具在具体的业务场景得出一个最好的平衡点。《谈谈对协程的理解

There will also be some work to ensure that coroutines don’t cause performance regressions.Improving a large C++ project with coroutines

知识点小贴士

=default=delete

=default is a new C++11 feature.
It means that you want to use the compiler-generated version of that function, so you don't need to specify a body.
You can also use = delete
to specify that you don't want the compiler to generate that function automatically.
With the introduction of move constructors and move assignment operators, the rules for when automatic versions of constructors, destructors and assignment operators are generated has become quite complex. Using = default and = delete makes things easier as you don't need to remember the rules: you just say what you want to happen.
= delete is stronger: It means, using that function is forbidden, though it still takes part in overload resolution.

用不到的

Boost Fusion,heterogeneous containers,不知道性能如何,应该用不到。

杂项

libgo这个项目的make系统可以自动生成C++项目,很不错,值得参考。

参考

[1] boost.coroutine学习笔记

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,425评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,058评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,186评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,848评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,249评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,554评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,830评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,536评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,239评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,505评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,004评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,346评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,999评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,060评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,821评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,574评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,480评论 2 267

推荐阅读更多精彩内容