lua学习之coroutine

本文深入探讨Lua中的协同程序(coroutine),解释了其基本概念和常用函数,如create、yield、resume和status,并通过解决生产者-消费者问题和实现迭代器来展示其实际应用。

本文介绍了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放在一个协同程序中,并创建迭代器函数,迭代器只是简单地唤醒协同程序,让它产生下一个元素。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值