协程(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(); // 等待协程退出