跳到主要内容
版本:release

协程(Coroutine)

不同语言对协程的定义各不相同,指代的也是不同的概念。

在 GS 中的协程(Coroutine,后文直接使用的协程指的就是GS 的协程) 是用户态线程, 如果不考虑技术上的细节,其行为更类似于线程。之所以称之为用户态线程,是因为协程 的调度是由 GS 中的协程调度器执行的,调度过程发生在用户态(相对于内核态)。

协程的生命周期

协程的创建

使用 coroutine.create... 系列方法可以指定协程的入口函数和参数,以及协程的名称和所属的

协程的名称可以是匿名的。推荐以协程的用途、创建的地方等要素命名,做出区分,以便能从协程的名称反推找到协程的创建位置。

协程的入口函数应当能够在指定的域上运行,并且需要给足参数数量。协程自创建时开始运行。当协程的入口函数正常结束或异常抛出时,协程的运行即告终止。

coroutine.create(nil, (){ printf("Helloworld~") });

协程的运行

根据创建方式不同,协程会在调度器或者一个独立的线程上执行。无论是何种方式调度运行的协程,其都需要遵守以下原则:

  • 同一时刻,一个域上只有一个协程在运行,其他协程无法进入该域,将被挂起
  • 一般而言,一个协程同时只会占据一个域(特殊情况见 带有优先级的域机制),当协程尝试进入一个域之前,必须先放弃当前的域。
  • 占据不同域的协程可以并发地运行

协程在运行的过程中,可能会因为各种原因而进入挂起状态(例如 socket.recv 没有收到东西而阻塞),此时协程也会将自己占用的域让出。

协程的终止

协程的终止有三种方式:

  • 协程的入口函数正常返回
  • 异常被抛出到入口函数之外,此异常被 GS 内部处理,协程被终止
  • 因为外部终止协程(退出 GS 或手动调用终止协程的接口)

在开发时,请将手动从外部终止协程视为一种禁忌,正确的做法应该是让协程自己收到需要退出的 “通知” 后自己退出,例如:

queue q = quque.create("example")
coroutine co = coroutine.create("recv_co", (){
while (true)
{
mixed msg = q.receive();
if (msg == nil) break; // 通过发送 nil 来通知协程退出

// 处理 msg
// ...
}
});

// 其他地方
q.send(some_work());
q.send(some_other_work());
// ...
q.send(nil); // 发送 nil 来通知协程退出
co.wait(); // 等待协程退出