# 15 Lua 协同程序\(coroutine\)
## 什么是协程\(coroutine\)?
> (1)线程
首先复习一下多线程。我们都知道线程——Thread。每一个线程都代表一个执行序列。
当我们在程序中创建多线程的时候,看起来,同一时刻多个线程是同时执行的,不过实质上多个线程是并发的,因为只有一个CPU,所以实质上同一个时刻只有一个线程在执行。
在一个时间片内执行哪个线程是不确定的,我们可以控制线程的优先级,不过真正的线程调度由CPU的调度决定。
> (2)协程
那什么是协程呢?协程跟线程都代表一个执行序列。不同的是,协程把线程中不确定的地方尽可能的去掉,执行序列间的切换不再由CPU隐藏的进行,而是由程序显式的进行。
所以,使用协程实现并发,需要多个协程彼此协作。
### 基本语法
| 方法 | 描述 |
| :--- | :--- |
| coroutine.create\(\) | 创建coroutine,返回coroutine, 参数是一个函数,当和resume配合使用的时候就唤醒函数调用 |
| coroutine.resume\(\) | 重启coroutine,和create配合使用 |
| coroutine.yield\(\) | 挂起coroutine,将coroutine设置为挂起状态,这个和resume配合使用能有很多有用的效果 |
| coroutine.status\(\) | 查看coroutine的状态 注:coroutine的状态有三种:dead,suspend,running,具体什么时候有这样的状态请参考下面的程序 |
| coroutine.wrap() | 创建coroutine,返回一个函数,一旦你调用这个函数,就进入coroutine,和create功能重复 |
| coroutine.running\(\) | 返回正在跑的coroutine,一个coroutine就是一个线程,当使用running的时候,就是返回一个corouting的线程号 |
###
### **三、coroutine库详解**
> \(1\)coroutine.create \(f\)
传一个函数参数,用来创建协程。返回一个“thread”对象。
> \(2\)coroutine.isyieldable \(\)
如果正在运行的协程可以让出,则返回真。值得注意的是,只有主协程(线程)和C函数中是无法让出的。
> \(3\)coroutine.resume \(co \[, val1, ···\]\)
这是一个非常重要的函数。用来启动或再次启动一个协程,使其由挂起状态变成运行状态。
可以这么说,resume函数相当于在执行协程中的方法。参数Val1...是执行协程co时传递给协程的方法。
#### **首次执行协程co时,参数Val1...会传递给协程co的函数;**
#### **再次执行协程co时,参数Val1...会作为给协程co中上一次yeild的返回值。**
不知道这句话大家理解了没,这是协程的核心。如果没理解也不用急,继续往下看,稍后我会详细解释。
resume函数返回什么呢?有3种情况:
1)、如果协程co的函数执行完毕,协程正常终止,`resume` 返回 true和函数的返回值。
2)、如果协程co的函数执行过程中,协程让出了(调用了yeild\(\)方法),那么resume返回true和协程中调用yeild传入的参数。
3)、如果协程co的函数执行过程中发生错误,resume返回false与错误消息。
可以看到resume无论如何都不会导致程序崩溃。它是在保护模式下执行的。
> \(4\)coroutine.running \(\)
用来判断当前执行的协程是不是主线程,如果是,就返回true。
> \(5\)coroutine.status \(co\)
返回一个字符串,表示协程的状态。有4种状态:
1)、running。如果在协程的函数中调用status,传入协程自身的句柄,那么执行到这里的时候才会返回running状态。
2)、suspended。如果协程还未结束,即自身调用了yeild或还没开始运行,那么就是suspended状态。
3)、normal。如果协程A resume 协程B时,协程A处于的状态为normal。在协程B的执行过程中,协程A就一直处于normal状态。因为它这时候既不是挂起状态、也不是运行状态。
4)、dead。如果一个协程发生错误结束,或正常终止。那么就处于dead状态。如果这时候对它调用resume,将返回false和错误消息。
> \(6\)coroutine.wrap \(f\)
wrap\(\)也是用来创建协程的。只不过这个协程的句柄是隐藏的。跟create\(\)的区别在于:
1)、wrap\(\)返回的是一个函数,每次调用这个函数相当于调用coroutine.resume\(\)。
2)、调用这个函数相当于在执行resume\(\)函数。
3)、调用这个函数时传入的参数,就相当于在调用resume时传入的除协程的句柄外的其他参数。
4)、调用这个函数时,跟resume不同的是,它并不是在保护模式下执行的,若执行崩溃会直接向外抛出。
> \(7\)coroutine.yield \(···\)
使正在执行的函数挂起。
** 传递给yeild的参数会作为resume的额外返回值。**
** 同时,如果对该协程不是第一次执行resume,resume函数传入的参数将会作为yield的返回值。**
### 以下实例演示了以上各个方法的用法:
例子1:简单实用resume、yield,如下:
```lua
--创建一个协程coco
coco = coroutine.create( function (a, b)
print ("resume args:"..a..","..b)
--协程被挂起
yreturn = coroutine.yield() -- yreturn 应该是下次resume唤醒传递进来的参数
print ("yretur:"..yreturn)
end)
--第一次启动coco协程
print (coroutine.resume(coco,0,1))
--第二次启动coco协程
print (coroutine.resume(coco, "第二次传递的参数,作为上一次yield的返回值"))
```
输出结果为:
```lua
resume args:0,1
true
yretur:第二次传递的参数,作为上一次yield的返回值
true
```
例子2:简单使用wrap,如下:
```lua
coco2 = coroutine.wrap(function (a, b)
print ("warp resume args:"..a..","..b)
yreturn = coroutine.yield()
print ("continue "..yreturn)
end)
print (type(coco2))
coco2(0,1)
coco2(3)
```
输出结果:
```lua
function
warp resume args:0,1
continue 3
```
例子3:多个协程配合使用
```lua
function status()
print("Co1's status:"..coroutine.status(co1)..",".."co2's status:"..coroutine.status(co2))
end
co1 = coroutine.create(function (a)
print ("arg is :"..a)
status()
local stat, rere = coroutine.resume(co2, "2")
print ("resume's return is "..rere)
status()
local stat2,rere2 = coroutine.resume(co2, "4")
print ("resume's return is"..rere2)
local arg = coroutine.yield("6")
end)
co2 = coroutine.create(function (a)
print ("arg is :"..a)
status()
local rey = coroutine.yield("3")
print ("yeild's return is ".. rey)
status()
coroutine.yield("5")
end)
stat,main_ret = coroutine.resume(co1, "1")
status()
print ("last return is "..main_ret)
```
结果如下:
```lua
arg is :1
Co1's status:running,co2's status:suspended
arg is :2
Co1's status:normal,co2's status:running
resume's return is 3
Co1's status:running,co2's status:suspended
yeild's return is 4
Co1's status:normal,co2's status:running
resume's return is5
Co1's status:suspended,co2's status:suspended
last return is 6
```
例子4:多个协程配合使用
```lua
function foo(a)
print ("foo", a)
return coroutine.yield(2*a)
end
co = coroutine.create(function(a, b)
print("co-body", a, b)
local r = foo(a+1)
print("co-body", r)
local r, s = coroutine.yield(a+b, a-b)
print ("co-body", r, s)
return b, "end"
end)
```
```lua
co-body 1 10
foo 2
main true 4
co-body r
main true 11 -9
co-body x y
main true 10 end
main false cannot resume dead coroutine
```
- 1 Lua介绍及环境
- 2 基本语法
- 3 数据类型
- 4 Lua 变量
- 5 循环
- 6 流程控制
- 7 函数
- 8 运算符
- 9 字符串
- 10 数组
- 11 迭代器
- 12 table
- 13 Lua 模块与包
- 14 Lua 元表(Metatable)
- 14.1 元表案例
- 15 Lua 协同程序(coroutine)
- 16 Lua 文件IO
- 17 Lua 面向对象
- 17.1 类
- 17.2 继承
- 17.3 封装
- 18 Lua 与 Mysql
- 19 Lua 与 redis
- 20 Lua 与 JSON
- 21 Lua 与 http
- 22 Lua 与 Nginx
- 22.1 Nginx_Lua的安装及环境
- 22.2 ngx_lua API(全表)
- 22.3 常用命令介绍
- 22 Lua 人工智能
- (1) Torch的安装
- (2)Tensor
- Lua与C混合编程