Skip to content

协同程序(coroutine)

协同程序(coroutine)是 Lua 中实现并发的机制,它允许在不同的执行流之间切换。本章节将介绍 Lua 中协同程序的概念、使用方法和常见应用。

协同程序的基本概念

协同程序是一种特殊的函数,它可以在执行过程中暂停,然后在稍后的某个时间点恢复执行。与线程不同,协同程序是协作式的,而不是抢占式的,也就是说,协同程序只有在显式地调用 yield 函数时才会暂停执行。

协同程序的创建和使用

创建协同程序

使用 coroutine.create 函数创建协同程序:

lua
local co = coroutine.create(function()
  print("协同程序开始执行")
  coroutine.yield()
  print("协同程序恢复执行")
end)

启动协同程序

使用 coroutine.resume 函数启动或恢复协同程序:

lua
print("启动协同程序")
coroutine.resume(co)
print("协同程序暂停")
coroutine.resume(co)
print("协同程序执行完毕")

输出:

启动协同程序
协同程序开始执行
协同程序暂停
协同程序恢复执行
协同程序执行完毕

协同程序的状态

协同程序有四种状态:

  • suspended:协同程序已创建但尚未启动,或已暂停
  • running:协同程序正在执行
  • dead:协同程序执行完毕
  • normal:协同程序正在执行,但不是当前协同程序

使用 coroutine.status 函数查看协同程序的状态:

lua
local co = coroutine.create(function()
  print("协同程序开始执行")
  coroutine.yield()
  print("协同程序恢复执行")
end)

print(coroutine.status(co))  -- 输出 suspended

coroutine.resume(co)
print(coroutine.status(co))  -- 输出 suspended

coroutine.resume(co)
print(coroutine.status(co))  -- 输出 dead

协同程序的参数和返回值

向协同程序传递参数

可以在 coroutine.resume 中向协同程序传递参数:

lua
local co = coroutine.create(function(a, b)
  print("协同程序开始执行,参数:" .. a .. ", " .. b)
  local c, d = coroutine.yield(a + b)
  print("协同程序恢复执行,参数:" .. c .. ", " .. d)
  return a + b + c + d
end)

local status, result = coroutine.resume(co, 10, 20)
print("第一次 resume 返回:" .. status .. ", " .. result)

local status, result = coroutine.resume(co, 30, 40)
print("第二次 resume 返回:" .. status .. ", " .. result)

输出:

协同程序开始执行,参数:10, 20
第一次 resume 返回:true, 30
协同程序恢复执行,参数:30, 40
第二次 resume 返回:true, 100

协同程序的返回值

协同程序的返回值会作为 coroutine.resume 的返回值:

lua
local co = coroutine.create(function()
  return "Hello", "World"
end)

local status, result1, result2 = coroutine.resume(co)
print(status, result1, result2)  -- 输出 true  Hello  World

协同程序的应用

示例 1:实现迭代器

lua
function values(t)
  local i = 0
  return coroutine.create(function()
    i = i + 1
    while i <= #t do
      coroutine.yield(t[i])
      i = i + 1
    end
  end)
end

local fruits = {"苹果", "香蕉", "橙子", "葡萄"}
local co = values(fruits)

while true do
  local status, value = coroutine.resume(co)
  if not status then
    break
  end
  print(value)
end

输出:

苹果
香蕉
橙子
葡萄

示例 2:实现生产者-消费者模式

lua
function producer()
  return coroutine.create(function()
    for i = 1, 5 do
      print("生产:" .. i)
      coroutine.yield(i)
    end
  end)
end

function consumer(prod)
  while true do
    local status, value = coroutine.resume(prod)
    if not status then
      break
    end
    print("消费:" .. value)
  end
end

local prod = producer()
consumer(prod)

输出:

生产:1
消费:1
生产:2
消费:2
生产:3
消费:3
生产:4
消费:4
生产:5
消费:5

示例 3:实现非阻塞 IO

lua
function readFile(filename)
  return coroutine.create(function()
    local file = io.open(filename, "r")
    if not file then
      coroutine.yield("文件不存在")
      return
    end
    local content = file:read("*")
    file:close()
    coroutine.yield(content)
  end)
end

local co = readFile("test.txt")
local status, content = coroutine.resume(co)
if status then
  print(content)
else
  print("错误:" .. content)
end

协同程序的高级应用

示例 1:实现状态机

lua
function stateMachine()
  return coroutine.create(function()
    while true do
      print("状态 1")
      coroutine.yield(1)
      print("状态 2")
      coroutine.yield(2)
      print("状态 3")
      coroutine.yield(3)
    end
  end)
end

local sm = stateMachine()
for i = 1, 5 do
  local status, state = coroutine.resume(sm)
  print("当前状态:" .. state)
end

输出:

状态 1
当前状态:1
状态 2
当前状态:2
状态 3
当前状态:3
状态 1
当前状态:1
状态 2
当前状态:2

示例 2:实现协程池

lua
local function createCoroutinePool(size, func)
  local pool = {}
  for i = 1, size do
    local co = coroutine.create(func)
    table.insert(pool, co)
  end
  return pool
end

local function worker()
  while true do
    local task = coroutine.yield()
    print("执行任务:" .. task)
  end
end

local pool = createCoroutinePool(3, worker)

local tasks = {"任务 1", "任务 2", "任务 3", "任务 4", "任务 5"}
for i, task in ipairs(tasks) do
  local co = pool[(i - 1) % #pool + 1]
  coroutine.resume(co, task)
end

输出:

执行任务:任务 1
执行任务:任务 2
执行任务:任务 3
执行任务:任务 4
执行任务:任务 5

小结

本章节介绍了 Lua 中协同程序的概念、创建和使用方法、状态、参数和返回值,以及协同程序在实现迭代器、生产者-消费者模式、非阻塞 IO、状态机和协程池等方面的应用。掌握协同程序的使用,对于编写 Lua 程序非常重要。