协程的好处有哪些?

在学 Lua, 基础是 Node 单线程的回调.. 觉得多线程很难懂
关注者
2449
被浏览
191582

52 个回答

我来说补充几个大家可能没有说到过的:

  • 1 历史上是先有协程,是OS用来模拟多任务并发,但是因为它是非抢占式的,导致多任务时间片不能公平分享,所以后来全部废弃了协程改成抢占式的线程。

  • 2 线程确实比协程性能更好。因为线程能利用多核达到真正的并行计算,如果任务设计的好,线程能几乎成倍的提高你的计算能力,说线程性能不好的很多是因为没有设计好导致大量的锁、切换、等待,这些很多都是应用层的问题。而协程因为是非抢占式,所以需要用户自己释放使用权来切换到其他协程,因此同一时间其实只有一个协程拥有运行权,相当于单线程的能力
  • 我们在x360、xbox1和ps4上做游戏的时候,开线程用来做数据加载、解压这种不需要或者很少需要数据同步的任务的时候效率杠杠的,而协程用来处理一些应用层逻辑调度的时候非常方便。官方文档也建议,协程只是为了老代码移植和兼容性,不推荐新代码使用。

  • 3 说协程性能好的,其实真正的原因是因为瓶颈在IO上面,而这个时候真正发挥不了线程的作用。

  • 4 协程的确可以减少callback的使用但是不能完全替换callback。基于事件驱动的编程里面反而不能发挥协程的作用而用callback更适合。想象一下用协程来写GUI的事件处理你怎么写。计算密集型的异步代码里面也只能用callback。而nodejs那种io瓶颈单任务流程用协程的确很适合,但是也需要callback做补充。

  • 5 LUA的标准版5.1里协程有一个内伤,不能跨c函数切协程,而JIT版没有这个问题。但是ios上面又不能用jit所以我直接把协程禁了免得到时候其他平台都是好的,到ios上就出奇怪的问题。

  • 6 状态机用协程其实也有问题,比如状态里面嵌套子状态,再由子状态切换到其他状态的子状态,开销和代码都会变差,反而不如经典的状态机简单明了高效

  • 7 其实nodejs早就有协程模块了,只是底层用的os的协程而不是v8里面js的协程,因此性能最多只有callback版本的80%左右,而且scale的很不好,但是代码是简单清晰多了。其实无论你是os的还是vm的,协程的开销必然比callback的开销大很多

差不多就补充这些。总之,协程不是万能药,选择合适的工具同样很重要
没有啥复杂的东西,考虑清楚需求,就可以很自然的衍生出这些解决方案。
  • 一开始大家想要同一时间执行那么三五个程序,大家能一块跑一跑。特别是UI什么的,别一上计算量比较大的玩意就跟死机一样。于是就有了并发,从程序员的角度可以看成是多个独立的逻辑流。内部可以是多cpu并行,也可以是单cpu时间分片,能快速的切换逻辑流,看起来像是大家一块跑的就行。

  • 但是一块跑就有问题了。我计算到一半,刚把多次方程解到最后一步,你突然插进来,我的中间状态咋办,我用来储存的内存被你覆盖了咋办?所以跑在一个cpu里面的并发都需要处理上下文切换的问题。进程就是这样抽象出来个一个概念,搭配虚拟内存、进程表之类的东西,用来管理独立的程序运行、切换。

  • 后来一电脑上有了好几个cpu,好咧,大家都别闲着,一人跑一进程。就是所谓的并行

  • 因为程序的使用涉及大量的计算机资源配置,把这活随意的交给用户程序,非常容易让整个系统分分钟被搞跪,资源分配也很难做到相对的公平。所以核心的操作需要陷入内核(kernel),切换到操作系统,让老大帮你来做。

  • 有的时候碰着I/O访问,阻塞了后面所有的计算。空着也是空着,老大就直接把CPU切换到其他进程,让人家先用着。当然除了I\O阻塞,还有时钟阻塞等等。一开始大家都这样弄,后来发现不成,太慢了。为啥呀,一切换进程得反复进入内核,置换掉一大堆状态。进程数一高,大部分系统资源就被进程切换给吃掉了。后来搞出线程的概念,大致意思就是,这个地方阻塞了,但我还有其他地方的逻辑流可以计算,这些逻辑流是共享一个地址空间的,不用特别麻烦的切换页表、刷新TLB,只要把寄存器刷新一遍就行,能比切换进程开销少点。

  • 如果连时钟阻塞、 线程切换这些功能我们都不需要了,自己在进程里面写一个逻辑流调度的东西。那么我们即可以利用到并发优势,又可以避免反复系统调用,还有进程切换造成的开销,分分钟给你上几千个逻辑流不费力。这就是用户态线程

  • 从上面可以看到,实现一个用户态线程有两个必须要处理的问题:一是碰着阻塞式I\O会导致整个进程被挂起;二是由于缺乏时钟阻塞,进程需要自己拥有调度线程的能力。如果一种实现使得每个线程需要自己通过调用某个方法,主动交出控制权。那么我们就称这种用户态线程是协作式的,即是协程

本质上协程就是用户空间下的线程。
为什么?