《Lua程序设计》9.1 协同程序基础 学习笔记
协同程序(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 协同程序基础 学习笔记的更多相关文章
- 《Lua程序设计》第1章 开始 学习笔记
1.1 程序块(chunk)每段代码(例如一个源代码文件或在交互模式中输入的一行代码),称为一个程序块.若使用命令行参数-i来启动Lua解释器,那么解释器就会在运行完指定程序块后进入交互模式.dofi ...
- 《Lua程序设计》第5章 函数 学习笔记
Lua为面向对象式的调用也提供了一种特殊的语法——冒号操作符.表达式o.foo(o, x)的另一种写法是o:foo(x),冒号操作符是调用o.foo时将o隐含地作为函数的第一个参数.Lua可以调用C语 ...
- 《Lua程序设计》第4章 语句 学习笔记
Lua中的常规语句包括:赋值.控制结构和过程调用.Lua还支持一些不太常见的语句,如:多重赋值(multiple assignment) 和 局部变量声明.4.1 赋值Lua允许“多重赋值”,也就是一 ...
- 《Lua程序设计》第3章 表达式 学习笔记
3.1 算术操作符“+”(加法).“-”(减法).“*”(乘法).“/”(除法).“^”(指数).“%”(取模).3.2 关系运算符< > <= >= == ~=3.3 逻辑操 ...
- 尚学堂JAVA基础学习笔记
目录 尚学堂JAVA基础学习笔记 写在前面 第1章 JAVA入门 第2章 数据类型和运算符 第3章 控制语句 第4章 Java面向对象基础 1. 面向对象基础 2. 面向对象的内存分析 3. 构造方法 ...
- 1.C#基础学习笔记3---C#字符串(转义符和内存存储无关)
技术qq交流群:JavaDream:251572072 教程下载,在线交流:创梦IT社区:www.credream.com ------------------------------------- ...
- 0003.5-20180422-自动化第四章-python基础学习笔记--脚本
0003.5-20180422-自动化第四章-python基础学习笔记--脚本 1-shopping """ v = [ {"name": " ...
- C#RabbitMQ基础学习笔记
RabbitMQ基础学习笔记(C#代码示例) 一.定义: MQ是MessageQueue,消息队列的简称(是流行的开源消息队列系统,利用erlang语言开发).MQ是一种应用程序对应用程序的通信方法. ...
- 基础学习笔记之opencv(6):实现将图片生成视频
基础学习笔记之opencv(6):实现将图片生成视频 在做实验的过程中.难免会读视频中的图片用来处理,相反将处理好的图片又整理输出为一个视频文件也是非经常常使用的. 以下就来讲讲基于opencv的C+ ...
随机推荐
- python opencv 学习笔记
图片缩放 image=cv2.imread('test.jpg') res=cv2.resize(image,(32,32),interpolation=cv2.INTER_CUBIC) cv2. ...
- VS下关于 _CRT_SECURE_NO_WARNINGS 问题的分析与解决
一.问题的起因 六月下旬,老师布置了有关图形学的课设,于是我将我在VS2013中写好的代码进行编译,结果得到了以下信息: 二.解决方法 1,按照英文提示,我先将fopen改成了fopen_s后,发现错 ...
- Lucene系列一:搜索引擎核心理论
一.为什么需要搜索引擎 问题1:数据库索引的原理是怎样的? 索引原理:对列值创建排序存储,数据结构={列值.行地址}.在有序数据列表中就可以利用二分查找快速找到要查找的行的地址,再根据地址直接取行数据 ...
- Java如何使用重载方法处理异常?
在Java编程中,如何使用重载方法处理异常? 此示例显示如何使用重载方法来处理异常.需要在每个方法中使用try catch块. package com.yiibai; public class Exc ...
- 图形界面至少要有一个顶级Swing容器
图形界面至少要有一个顶级Swing容器 顶级Swing容器为其它Swing组件在屏幕上的绘制和处理事件提供支持 常用的顶级容器: JFrame(框架):表示主程序窗口 JDialog(对话框):每个J ...
- perl学习-运算符添加引号
这个比较有意思,在其它语言中好像没有特别提到 Perl 引号运算符如下表所示. 运算符描述实例 q{ }为字符串添加单引号q{abcd} 结果为 'abcd' qq{ }为字符串添加双引号qq{abc ...
- MySQL谨慎使用"replace into"
From: http://blog.xupeng.me/2013/10/11/mysql-replace-into-trap/ MySQL 对 SQL 有很多扩展,有些用起来很方便,但有一些被误用之后 ...
- Java对象的序列化与反序列化-Json篇
说到Java对象的序列化与反序列化,我们首先想到的应该是Java的Serializable接口,这玩意在两个系统之间的DTO对象里面可能会用到,用于系统之间的数据传输.或者在RPC(远程方法调用)时可 ...
- phd文献阅读日志-博一上学期
为了记住并提醒自己阅读文献,进行了记录(这些论文都是我看过理解的),论文一直在更新中. 博一上学期: 1.week 6,2017.10.16 2014-Automatic Semantic Model ...
- mysql Communication link failure, message from server: "Can't get hostname for your address"
在连接mysql jdbc时候,抛出了 com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException: Communicat ...