Lua 协程 (coroutine)

协程(coroutine) 与线程类似:

拥有独立的堆栈,独立的局部变量,独立的指令指针
同时又与其它协同程序共享全局变量和其它大部分东西

协程是非常强大的功能,但是用起来也很复杂

线程和协同程序区别

线程与协同程序的主要区别在于

一个具有多个线程的程序可以同时运行几个线程
而协同程序却需要彼此协作的运行

协程的特点在于

在任一指定时刻只有一个协同程序在运行,并且这个正在运行的协程只有在明确的被要求挂起的时候才会被挂起

下列是 Lua 中 协程(corouting) 的常用函数

方法 描述
coroutine.create() 创建 coroutine,返回 coroutine, 参数是一个函数
coroutine.resume() 重启 coroutine
coroutine.yield() 挂起 coroutine,将 coroutine 设置为挂起状态
coroutine.status() 查看 coroutine 的状态
coroutine的状态有三种:dead,suspend,running
coroutine.wrap() 创建 coroutine,返回一个函数
调用 wrap 函数,就进入 coroutine,和 create 功能一样
coroutine.running() 返回正在运行的 coroutine,一个 coroutine 就是一个线程
当使用 running 的时候,就是返回一个 corouting 的线程号

范例

下面的范例演示了 协程(corouting) 各个方法的使用:

-- !/usr/bin/lua
-- -*- encoding:utf-8 -*-
-- filename: main.lua
-- author: 简单教程(www.twle.cn)
-- Copyright © 2015-2065 www.twle.cn. All rights reserved.

local co = coroutine.create(
  function(i)
      print(i);
  end
)

coroutine.resume(co, 1)   -- 1
print(coroutine.status(co))  -- dead

print("----create & resume end------")

co = coroutine.wrap(
  function(i)
      print(i*2);
  end
)

co(1)

print("----- wrap end -------")

co2 = coroutine.create(
  function()
      for i=1,10 do
          print(i)
          if i == 3 then
              print(coroutine.status(co2))  --running
              print(coroutine.running()) --thread:XXXXXX
          end
          coroutine.yield()
      end
  end
)

coroutine.resume(co2) --1
coroutine.resume(co2) --2
coroutine.resume(co2) --3

print(coroutine.status(co2))   -- suspended
print(coroutine.running()) 

print("----------")

运行以上 Lua 脚本,输出结果如下:

$ lua main.lua
1
dead
----create & resume end------
2
----- wrap end -------
1
2
3
running
thread: 0x7fbfc2c07758  false
suspended
thread: 0x7fbfc3001008  true
----------

coroutine.running 可以看出:协程(coroutine) 的底层实现就是一个线程

调用 coroutine.create 函数生成一个 coroutine 其实就是向新线程注册了一个事件

当使用 coroutine.resume 函数触发事件的时候,create 的 coroutine 函数就被执行了 当 coroutine 遇到 yield 的时候就代表挂起当前线程,等候再次 resume 触发事件


范例 2 : coroutine 的使用

-- !/usr/bin/lua
-- -*- encoding:utf-8 -*-
-- filename: main.lua
-- author: 简单教程(www.twle.cn)
-- Copyright © 2015-2065 www.twle.cn. All rights reserved.

function mul2 ( i )
  print("mul2 函数输出:", i)
  return coroutine.yield( 2 * i) -- 返回  2*i 的值
end

local co = coroutine.create(function (a , b)
  print("第一次协同程序执行输出", a, b) -- co-body 1 10
  local r = mul2(a + 1)

  print("第二次协同程序执行输出", r)
  local r, s = coroutine.yield(a + b, a - b)  -- a,b的值为第一次调用协同程序时传入

  print("第三次协同程序执行输出", r, s)
  return b, "结束协同程序"                   -- b的值为第二次调用协同程序时传入
end)

print("main", coroutine.resume(co, 1, 10)) -- true, 4
print("\n--分割线----")
print("main", coroutine.resume(co, "r")) -- true 11 -9
print("\n---分割线---")
print("main", coroutine.resume(co, "x", "y")) -- true 10 end
print("\n---分割线---")
print("main", coroutine.resume(co, "x", "y")) -- cannot resume dead coroutine
print("\n---分割线---")

运行以上 Lua 脚本,输出结果如下:

$ lua main.lua
第一次协同程序执行输出 1   10
mul2 函数输出:  2
main    true    4

--分割线----
第二次协同程序执行输出 r
main    true    11  -9

---分割线---
第三次协同程序执行输出 x   y
main    true    10  结束协同程序

---分割线---
main    false   cannot resume dead coroutine

---分割线---

从范例运行的结果可知:

  1. 调用 resume,将协同程序唤醒,resume 操作成功返回 true,否则返回 false

  2. 协程运行

  3. 运行到 yield 语句;

  4. yield 挂起协程,第一次 resume 返回;(注意:此处 yield 返回,参数是 resume 的参数)

  5. 第二次 resume,再次唤醒协同程序;(注意:此处 resume 的参数中,除了第一个参数,剩下的参数将作为 yield 的参数)

  6. yield返回

  7. 协同程序继续运行

  8. 如果使用的协同程序继续运行完成后继续调用 resume方法则输出:cannot resume dead coroutine

resume 和 yield 的配合强大之处在于:

resume 处于主程中,它将外部状态(数据)传入到协同程序内部
而 yield 则将内部的状态(数据)返回到主程中

生产者-消费者问题

Lua 中的 协程( coroutine ) 是用来解决 生产者 - 消费者经典问题最好的解决方案

-- !/usr/bin/lua
-- -*- encoding:utf-8 -*-
-- filename: main.lua
-- author: 简单教程(www.twle.cn)
-- Copyright © 2015-2065 www.twle.cn. All rights reserved.


local _new_productor

function productor()
     local i = 0
     while true do
          i = i + 2
          send(i)     -- 将生产的物品发送给消费者
     end
end

function consumer()
     while true do
          local i = receive()     -- 从生产者那里得到物品
          print(i)
     end
end

function receive()
     local status, value = coroutine.resume( _new_productor )
     return value
end

function send(x)
     coroutine.yield(x)     -- x 表示需要发送的值,值返回以后,就挂起该协程
end

-- 启动程序
_new_productor = coroutine.create(productor)
consumer()

运行以上 Lua 脚本,输出结果如下

2
4
6
8
10
12
14
...
关于   |   FAQ   |   我们的愿景   |   广告投放   |  博客

  简单教程,简单编程 - IT 入门首选站

Copyright © 2013-2022 简单教程 twle.cn All Rights Reserved.