浅谈Lua协程 [ 奔跑的蜗牛 ]

1.7k 词

撰写于 2018-05-09

    <span id="post-title-updated">修改于 2018-05-10</span>
  
  
  
</p>

<p>很多脚本语言都有线程的概念,lua也不例外,但是lua的线程有点特殊,不能算作是真正的线程,叫做协程(coroutine),一般意义上的线程有如下特征:每个线程都有自己的堆栈;可以同时执行(多核);抢占式执行,无须手动唤醒。但是协程不一样。</p>

协程特点

  1. 有自己的的堆栈和局部变量
  2. 需要手动切换协程之间的执行
  3. 同一时刻只能有一个协程执行(无论是否是多核)

coroutine库

  1. coroutine.create(f)
    传递一个函数f,创建一个协程,返回协程句柄。

  2. coroutine.isyieldable()
    正在运行的协程是否可以让出,如果可以,返回true,只有主协程和C函数是无法让出的。

  3. coroutine.resume(co, [val1,…])
    启动或者再次启动一个协程,由挂起状态(suspended)变为运行状态(running)
    注:
    首次启动协程会把参数[val1,…],传递给协程co的函数;
    再次启动协程会把参数[val1,…],作为给协程co上一次yeild的返回值。
    返回值:

    • 如果协程co的函数执行完毕,则协程正常终止,resume返回true和函数的返回值
    • 如果协程执行过程中,协程挂起了,即调用yeild(),resume返回true和调用yeild传入的参数
    • 如果协程执行过程中发生了错误,resume返回false和错误消息
  4. coroutine.yeild(…)
    使正在实行的协程挂起
    注:
    传递给yeild的参数会作为resume的额外返回值

  5. coroutine.running()
    判断当前线程是不是主线程,如果是,返回true

  6. coroutine.status(co)
    返回string,表示协程co的状态

    • running:由于当前只能有一个协程在执行,只有在协程的函数中使用status,传入自身句柄,执行到此处才会返回true,作用不大
    • suspended:如果协程创建了还没有执行或者调用了yeild,则返回该状态
    • normal:在协程A的函数里面resume了协程B,在B的执行过程中,则A就是normal状态。此时A的状态既不是运行,也不是挂起
    • dead:协程在执行过程中发生了错误,或者正常执行完毕,就是dead状态。调用resume返回false和错误消息。
  7. coroutine.wrap(f)
    这个就是一个语法糖。功能同coroutine.create(f)。但是不返回协程的句柄,而是返回了一个函数。

    • 每次调用返回的函数,就相当于调用了coroutine.resume()
    • 如果该协程的函数执行错误,不会像resume一样,在保护模式下进行,返回false,而是直接崩溃,向外跑出异常。

以上都是摘自https://www.cnblogs.com/zrtqsk/p/4374360.html

协程的优点

通过以上的介绍可以发现,lua的协程很简单,由于是手动控制协程的切换,不用考虑因资源抢占引起的问题。

协程的缺点

因为同一时间只有一个协程拥有执行权,只相当于单线程的能力,也导致了时间片不能公平分享,很是鸡肋,功能上也跟callback有重复。而且不能跨c函数切换协程。总之线程是系统级实现,而协程只是在应用层实现。

知乎大神的评论:
协程不过是一个逻辑控制需求。一些语言原生支持,不支持也可以用原有的材料构建出来。协程的实现,无非是你要维护一组局部状态,在重新进入协程前,保证这些状态不被改变,你能顺利定位到之前的位置。你平时所写的一些逻辑控制代码,经典如状态机或对象等,也许就已经是一种“协程”了。区别在于是否精巧,适用条件是否苛刻,使用是否方便,效率是否足够罢了。面向对象中的对象,函数式语言中过程的chunk实现,都跟协程有些相似的结构。这些语言的表达足够丰富,有没有协程,倒不构成问题。真要说起来,我觉得协程的最大的好处是在写过程式(命令式)风格的代码时,很好的简化了逻辑的流程。

还是不能理解协程的真谛!或许真的很强大,我不能理解!