Go游戏服务器开发的一些思考(十):goroutine和coroutine

u013272009 · · 4500 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

概要

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系统已经是完全可行。


有疑问加站长微信联系(非本文作者)

本文来自:CSDN博客

感谢作者:u013272009

查看原文:Go游戏服务器开发的一些思考(十):goroutine和coroutine

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

4500 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传