概要
go语言的特色之一就是goroutine。也就是go协程。由于协程这个东西在go语言之前,用到相对比较少,大家对协程的理解程度不一,或有偏差。比如本人刚接触goroutine时,就对其比较畏惧,因为不知道它到底是如何运作的。因此有必要深入了解下什么是协程,它的今生前世,以及工作原理
前世
作为服务器端程序员,一般来说,都会使用过、或者自己实现过 “通用的异步任务系统” ,来达成安全方便的多线程使用。通常来讲,比较典型的会是基于actor模型及回调的方式制定差异。
这里我们主要来考察下其不足之处。下面简单的画一下 任务对象和线程间的关系:
------- ------- -------
| task1 | | task2 | .... | taskn | => thread1
------- ------- -------
------- ------- -------
| taska | | taskb | .... | taskz | => thread2
------- ------- -------
...
------- ------- -------
| taskA | | taskB | .... | taskZ | => threadn
------- ------- -------
如图所示,一般会开n个线程来处理,每个任务按一定的策略被投递到线程中执行,任务完成后,触发异步回调。
假设 任务中,有一定比例的任务是IO阻碍的。那么线程在执行这个任务时,会被挂起。导致后面的任务也只能等待。
总结下其不足的地方:
* CPU能力不能达到完全释放
* IO阻塞任务的数量与线程被系统调度成正比
* 任务完成需要回调方式,编程上不直观、比较难受(若没有前两条不足,这个大概也不会出现。背锅侠是也...)
(ps. 协程起源于单CPU单线程时代。要解决的问题,同这里表述的。多线程后,为了榨干CPU处理能力,协程开始用于多线程系统,如这里描述的)
程序员的智慧
那么如何才能把 “异步任务系统” 做的完美呢。假设能这样就好了:
* IO阻塞任务,执行到阻塞语句时,系统可以下达它的IO指令又可以把它拎出来,重新插到任务队列最后。
假设能实现这样的效果。那么上述的不足也就不存在了:
* 阻塞的任务因为拎出来了,后续的任务可以继续欢快的在该线程上跑了
* 阻塞的任务因为拎出来了,线程也不会被阻塞,也就不会被系统调度出去了
那么如何才能做到。聪明的程序员很快找到了解决方案:
* 将IO阻塞的API hook掉,换成异步实现
* 模仿操作系统线程的调度方法,实现任务的切出切进
这里点下,为什么需要 “实现任务的切出切进”。由于把IO阻塞的API hook掉,换成异步实现。如果让该任务继续执行的话,就会改变该任务的流程。因此必须切出去。等再次切进来时,检查IO事件是否已经到了。到了则如同 IO阻塞完毕,继续执行任务流程。否则再次切出。
今生
于是很多产品出来了。
首先是操作系统,推出了用户态 上下文切换API
操作系统 | API |
---|---|
window | 纤程(Fiber系列API) |
linux | ucontext系列API |
其他 | 大厂直接自己汇编搞定,高级定制(比如go …) |
然后 很多开源库。这里介绍几个有名的
开源库 | 介绍 |
---|---|
boost | boost::context、boost::coroutine。仅跨平台提供协程基础功能。 |
libco | 腾讯的协程库。明显的阉割版放出。没有提供上层封装使用层代码 |
libgo | 魅族的协程库。和goroutine使用功能上无限接近。 |
libtask | goroutine前身。goroutine一出,大家惊呆了,原来封装的好,也可以这么好用。 |
顺便吐槽下,翻下libco、libgo、libtask,会发现代码中只有task。而到了市面上则变成了 协程(coroutine)。玩起概念来…
goroutine、libgo、libco 比较
开源库 | HOOK情况 | 协程大小 | 功能完成度(goroutine用法为参照) | 代码可读性 |
---|---|---|---|---|
goroutine | 全部hook | 8K开始,动态增加,分段栈实现 | 100% | go代码太庞大,还没找到… |
libco | socket系列API | 可制定大小,共享栈实现 | 最基本的协程功能 | 代码量小,比较难看,没看 |
libgo | socket系列API | 可制定大小,没有特殊实现 | 接近100% | 代码量小,可读性很高,基本看懂 |
(ps.据libgo的作者说,libgo协程栈用了 “os的虚拟内存机制,是动态增长的”。待考究优略 )
C++框架新的可能
C++ libgo库设计的非常棒,还原 goroutine 程度 接近100%(当然这里指的是用法。稳定性、可靠性还需很多人、产品的检验)。因此让C++引擎具备实用的cocoutine系统已经是完全可行。
有疑问加站长微信联系(非本文作者)