本文介绍了lua中的coroutine(协同程序),首先是协同程序的基本概念和常用的函数,然后展示了一个通过coroutine来解决”生产者—消费者“问题的示例,最后现了一个迭代器。
一、基本概念和常用函数
协同程序(coroutine)与线程类似,它拥有自己独立的栈、局部变量和指令指针,同时又与其他协同程序共享全局变量和其他大部分东西。协程和线程的主要区别是,一个具有多个线程的程序可以同时运行几个线程,而协同程序需要彼此协作地运行。也就是说,一个具有多个协同程序的程序在任意时刻只能运行一个协同程序,并且正在运行的协同程序只会在其显式地要求挂起时,它的执行才会暂停。
首先介绍一下关于协同程序的几个函数:
coroutine.create(f):用于创建一个协同程序;它的参数是一个匿名函数,作为这个协同程序的主函数;返回值是该协同程序的控制器,是一个thread类型的值。
coroutine.yield(...):用于使一个协同程序挂起;它的参数是可变参数,作为对应的resume()返回值的一部分;它的返回值是对应的resume()传入的出第一个参数外的其他参数。
coroutine.resume(co, ...):用于启动或者再次启动一个协同程序;它的参数分为两部分,第一个参数是打算启动的协同程序,第二部分是一个可变参数,对于新创建的协同程序,这个可变参数会作为它的主函数的参数,对于不是新创建的协同程序,这个可变参数将作为对应yield的返回值;它的返回值包括两部分,首先是一个表示resume有没有出错的boolean值,然后是对应的yield()传入的参数或者协同程序主函数的返回值,在resume出错的情况下返回一条错误信息。
coroutine.status(co):查看协同程序的状态。一个协同程序可以处于4种不同的状态:挂起(suspend)、运行(running)、死亡(dead)和正常(normal)。一个协同程序在创建时处于挂起状态,通过函数coroutine.resume()可以使一个挂起的协同程序进入运行状态,它在执行到coroutine.yield()时进入挂起状态,在这个协同程序的主函数返回后处于死亡状态,当一个协同程序A唤醒另一个协同程序B时,协同程序A就处于正常状态。
coroutine.running():返回当前正在运行的协同程序。
下面通过一个例子来熟悉上边几个函数
co = coroutine.create(
function (a, b) --第一次resume调用时传入参数1, 10
print("coroutine:", coroutine.yield(a + b)) --(3)coroutine: 2 20
a, b = coroutine.yield(a - b) --a, b的值来自第三次resume传入的参数
print("status:", coroutine.status(co)) --(5)status: running
return a, b, a + b
end)
print("status:", coroutine.status(co)) --(1)status: suspended
print("main:", coroutine.resume(co, 1, 10)) --(2)main: true 11
print("main:", coroutine.resume(co, 2, 20)) --(4)main: true -9
print("main:", coroutine.resume(co, 3, 30)) --(6)main: true 3 30 33
print("status:", coroutine.status(co)) --(7)status: dead
print(coroutine.resume(co)) --(8)false cannot resume dead coroutine
首先我们创建了一个协同程序,在调用resume()之前,它的状态是挂起,第一次调用resume()之后,它进入运行状态,主函数的参数从resume()的参数获得,在遇到yield()时挂起,yield()的参数作为resume()的返回值的一部分,第二次调用resume(),这个协同程序又切换到运行状态,从上次yield()的地方继续执行,yield()的返回值是resume()参数中除去第一个参数剩下的部分,从打印的结果中可以看出来。在协同程序的主函数返回之后,可以发现它的状态变成了死亡。主函数所返回的值都作为了对应resume()的返回值。当对一个处于死亡状态的协同程序调用resume()时,resume()会返回false和一条错误信息。
二、生产者消费者问题
一个关于协同程序的经典示例就是”生产者—消费者“的问题。
function receive (prod)
local status, value = coroutine.resume(prod)
return value
end
function send(x)
coroutine.yield(x)
end
producer = coroutine.create(
function ()
local i = 0
while true do
local x = i --产生新的值
i = i + 1
send(x) --发送给消费者
end
end)
function consumer()
while true do
local x = receive(producer) --从生产者接收值
print(x) --消费新的值
end
end
consumer()这里将生产者设计为一个协同程序,当消费者需要一个新的值时,通过receive()函数中的resume()唤醒生产者,生产者产生一个新值,通过调用send()中的yield()将新值返回给消费者,之后生产者挂起,并等待消费者的再次唤醒。
三、协同程序实现迭代器
将循环迭代器视为“生产者—消费者”模式的一种特例,一个迭代器产出一些内容,而循环体消费这些内容。接下来编写了一个迭代器,它按照从小到大的顺序遍历两个数组中的元素,做法是将两个数组合并为一个数组,然后进行排序,代码如下:
function seq_gen(a, b)
local a = a or {}
local b = b or {}
local c = {}
for i = 1, #a do
table.insert(c, a[i])
end
for i = 1, #b do
table.insert(c, b[i])
end
table.sort(c)
for j = 1, #c do
coroutine.yield(c[j])
end
return nil
end
function seq(a, b)
local co = coroutine.create(function()
seq_gen(a, b)
end)
return function ()
local status, value = coroutine.resume(co)
return value
end
end
a = {23, 3, 45}
b = {2, 3, 51}
for i in seq(a, b) do
print(i)
end首先定义了一个函数seq_gen,它用于对两个数组进行合并和排序,并通过yield依次返回合并后数组中的元素,接下来定义了一个工厂函数seq,它将seq_gen放在一个协同程序中,并创建迭代器函数,迭代器只是简单地唤醒协同程序,让它产生下一个元素。
本文深入探讨Lua中的协同程序(coroutine),解释了其基本概念和常用函数,如create、yield、resume和status,并通过解决生产者-消费者问题和实现迭代器来展示其实际应用。
552

被折叠的 条评论
为什么被折叠?



