协同程序(coroutine)与线程(thread)差不多,也就是一条执行序列,拥有自己独立的栈、局部变量和指令指针,同时又与其他协同程序共享全局变量和其他大部分东西。从概念上讲线程与协同程序的主要区别在于,一个具有多个线程的程序可以同时运行几个线程,而协同程序却需要彼此协作地运行。就是说,一个具有多个协同程序的程序在任意时刻只能运行一个协同程序,并且正在运行的协同程序只会在其显式地要求挂起(suspend)时,它的执行才会暂停。

Lua将所有关于协同程序的函数放置在一个名为“coroutine”的table中。函数create用于创建新的协同程序,它只有一个参数,就是一个函数。该函数的代码就是协同程序所需执行的内容。create会返回一个thread类型的值,用以表示新的协同程序。通常create的参数是一个匿名函数,例如:

co = coroutine.create(function () print("hi") end)
print(co) --> thread: 0x8071d98

一个协同程序可以处于4种不同的状态:挂起(suspended)、运行(running)、死亡(dead)和正常(normal)。当创建一个协同程序时,它处于挂起状态。也就是说,协同程序不会在创建它时自动执行其内容。可以通过函数status来检查协同程序的状态:

print(coroutine.status(co))    --> suspended

函数coroutine.resume用于启动或再次启动一个协同程序的执行,并将其状态由挂起改为运行:

coroutine.resume(co)    --> hi

在本例中,协同程序的内容指示简单地打印了“hi”后便终止了,然后它就处于死亡状态,也就再也无法返回了:

print(coroutine.status(co))    --> dead

到目前为止,协同程序看上去还指示像一种复杂的函数调用方法。骑士协同程序的真正强大指出在于函数yield的使用上,该函数可以让一个运行中的协同程序挂起,而之后可以再恢复它的运行。请看下面的这个简单的示例:

co = coroutine.create(function ()
for i=, do
print("co", i)
coroutine.yield()
end
end)

现在当唤醒这个协同程序时,它就会开始执行,知道第一个yield:

coroutine.resume(co)    --> co  1

如果此时检查器状态,会发现协同程序处于挂起状态,因此可以再次恢复其运行:

print(coroutine.status(co))    --> suspended

从协同程序的角度看,所有在它挂起时发生的活动都发生在yield调用中。当恢复协同程序的执行时,对于yield的调用才最终返回。然后协同程序继续执行它的执行,知道下一个yield调用或执行结束:

coroutine.resume(co)    --> co  2
coroutine.resume(co) --> co 3
...
coroutine.resume(co) --> co 10
coroutine.resume(co) -- 什么都不打印

在最后一次调用resume时,协同程序的内容已经执行完毕,并已经返回。因此,这时协同程序处于死亡状态。如果试图再次恢复它的执行,resume将false及一条错误消息:

print(coroutine.resume(co))    --> false  cannot resume dead coroutine

resume是在保护模式中运行的。因此,如果在一个协同程序的执行中发生任何错误,Lua是不会显示错误消息的,而是将执行权返回给resume调用。
当一个协同程序A唤醒另一个协同程序B时,协同程序A就处于一种特殊状态,既不是挂起状态(无法继续A的执行),也不是运行状态(是B在运行)。所以将这时的状态称为“正常”状态。
Lua的协同程序还具有一项有用的机制,就是可以通过一对resume-yield来交换数据。在第一次调用resume时,并没有对应的yield在等待它,因此所有传递给resume的额外参数都将视为协同程序主函数的参数:

co = coroutine.create(function (a,b,c)
print("co", a,b,c)
end)
coroutine.resume(co, , , ) --> co 1 2 3

在resume调用返回的内容中,第一个值为true则表示没有错误,而后面所有的值都是对应yield传入的参数:

co = coroutine.create(function (a,b)
coroutine.yield(a+b, a-b)
end)
print(coroutine.resume(co, , )) --> true 30 10

与此对应的是,yield返回的额外值就是对应resume传入的参数:

co = coroutine.create (function ()
print("co", coroutine.yield())
end)
coroutine.resume(co)
coroutine.resume(co, , ) --> co 4 5

最后,当一个协同程序结束时,它的主函数所返回的值都将作为对应resume的返回值:

co = coroutine.create(function ()
return ,
end)
print(coroutine.resume(co)) --> true 6 7

很少在同一个协同程序中使用所有这些功能,但每种功能各有其用途。

Lua提供的是一种“非对称的协同程序(asymmetric coroutine)”。也就是说,Lua提供了两个函数来控制协同程序的执行,一个用于挂起执行,另一个用于恢复执行。而一些其他的语言则提供了“堆成的协同程序(symmetric coroutine)”,其中只有一个函数用于转让协同程序之间的执行权。

《Lua程序设计》9.1 协同程序基础 学习笔记的更多相关文章

  1. 《Lua程序设计》第1章 开始 学习笔记

    1.1 程序块(chunk)每段代码(例如一个源代码文件或在交互模式中输入的一行代码),称为一个程序块.若使用命令行参数-i来启动Lua解释器,那么解释器就会在运行完指定程序块后进入交互模式.dofi ...

  2. 《Lua程序设计》第5章 函数 学习笔记

    Lua为面向对象式的调用也提供了一种特殊的语法——冒号操作符.表达式o.foo(o, x)的另一种写法是o:foo(x),冒号操作符是调用o.foo时将o隐含地作为函数的第一个参数.Lua可以调用C语 ...

  3. 《Lua程序设计》第4章 语句 学习笔记

    Lua中的常规语句包括:赋值.控制结构和过程调用.Lua还支持一些不太常见的语句,如:多重赋值(multiple assignment) 和 局部变量声明.4.1 赋值Lua允许“多重赋值”,也就是一 ...

  4. 《Lua程序设计》第3章 表达式 学习笔记

    3.1 算术操作符“+”(加法).“-”(减法).“*”(乘法).“/”(除法).“^”(指数).“%”(取模).3.2 关系运算符< > <= >= == ~=3.3 逻辑操 ...

  5. 尚学堂JAVA基础学习笔记

    目录 尚学堂JAVA基础学习笔记 写在前面 第1章 JAVA入门 第2章 数据类型和运算符 第3章 控制语句 第4章 Java面向对象基础 1. 面向对象基础 2. 面向对象的内存分析 3. 构造方法 ...

  6. 1.C#基础学习笔记3---C#字符串(转义符和内存存储无关)

    技术qq交流群:JavaDream:251572072  教程下载,在线交流:创梦IT社区:www.credream.com ------------------------------------- ...

  7. 0003.5-20180422-自动化第四章-python基础学习笔记--脚本

    0003.5-20180422-自动化第四章-python基础学习笔记--脚本 1-shopping """ v = [ {"name": " ...

  8. C#RabbitMQ基础学习笔记

    RabbitMQ基础学习笔记(C#代码示例) 一.定义: MQ是MessageQueue,消息队列的简称(是流行的开源消息队列系统,利用erlang语言开发).MQ是一种应用程序对应用程序的通信方法. ...

  9. 基础学习笔记之opencv(6):实现将图片生成视频

    基础学习笔记之opencv(6):实现将图片生成视频 在做实验的过程中.难免会读视频中的图片用来处理,相反将处理好的图片又整理输出为一个视频文件也是非经常常使用的. 以下就来讲讲基于opencv的C+ ...

随机推荐

  1. SAP NUMBER RANGE维护配置object FBN1 Deletion only possible if status is initial

    背景: 错误日志: SAP FBN1 Deletion only possible if status is initial 场景: 如果目标机已有NUMBER RANGE 不为0,需要删除配置年为9 ...

  2. Spring Boot 启用Gzip压缩

    有两点需要注意 1.需要在application.properties里启用压缩,并设置压缩支持的格式(默认支持text/html等,但不支持application/json) server.comp ...

  3. 工作所用的日常 Git 命令

    几乎每个开发人员都在使用 Git,当然很可能是 GitHub.但大多数开发者大概有 99% 的时间只是使用这三个命令: #使用命令git add <file>,将文件添加到暂存区 git ...

  4. linux静止ping的方法

    ping是一个通信协议,是ip协议的一部分,tcp/ip 协议的一部分.利用它可以检查网络是否能够连通,用好它可以很好地帮助我们分析判定网络故障.应用格式为:Ping IP地址.但服务启用ping有时 ...

  5. 炫酷霸气的HTML5/jQuery应用及源码

    也许在5年前,HTML5还是一种很前卫的技术,大家还只是将它当做实验来看待,更别说产品应用了.但是现在HTML5已经非常流行,无论从PC端还是移动端,HTML5都扮演着非常重要的角色.今天我们要分享的 ...

  6. javascript报错集锦

    1.JS 异常之 missing ) after argument list 错误释疑报错原因:不是字符串就输出啦

  7. 常用Linq示例代码

    class Program { static void Main(string[] args) {   //1. Aggregate int[] testArr = new int[] { 1, 2, ...

  8. Blender 建模

    1.多图层切换 Blender也有图层的概念,我们在一个图层上建立了一个模型,可以在另外一个图层新建一个独立的模型.界面底部包含了Layer切换按钮.如下图所示: 当前我们正在操作第一个图层,如果想在 ...

  9. 5 -- Hibernate的基本用法 --1 3 流行的ORM框架简介

    ⊙ JPA : JPA本身只是一种ORM规范,并不是ORM产品.它是Java EE规范制定者向开源世界学习的结果.JPA实体与Hibernate PO十分相似,甚至JPA实体完全可作为Hibernat ...

  10. com.alibaba.fastjson.JSONObject

    package com.alibaba.fastjson; import java.util.Date; import java.util.List; import com.alibaba.fastj ...