lua中的协同程序

1.协同程序相关的几个函数

lua中的协同程序类似于线程,拥有自己的独立资源(独立栈,自己的指针和局部变量等),也和其他协同程序共享全局的资源(比如同属于同一个lua的luastate 的多个协同程序可以共享该luastate中的全局变量),但也和线程有不一样的地方,当一个程序拥有多个线程时,他们是并发执行的,是一种抢占式的关系,然而一个具有多个协同程序的程序,同一时刻只能有一个协同程序在运行,是一种彼此合作的关系,从而减少同步带来的一些问题。
lua中的协同主要有以下几个函数来操作:coroutine.create(para),coroutine.status(para),coroutine.resume(para),coroutine.yield().我们用coroutine.create(para)来创建一个协同,并打印一下创建出来的结果:
function coroutineTest()
	print("i am a coroutine program")
end
print(coroutine.create(coroutineTest))
显示的是我们创建的协同的地址.那我们程序中打印的那句话哪里去了呢?原来是因为协同创建之后是处在一个挂起的状态,我们可以通过coroutine.status()来查看一个协同的状态(挂起,运行,死亡,正常),我们可以使用coroutine.resume()来恢复其运行:
function coroutineTest()
	print("i am a coroutine program")
end
local co =coroutine.create(coroutineTest)
print(coroutine.status(co))
coroutine.resume(co)

我们打印了创建一个协同时的状态和协同程序结束后的状态。当一个协同程序里面唤醒另一个协同程序时,那前一个协同程序就处在一种特殊的状态,“”正常“”状态.
其实,协同程序的强大在于他的yield机制,他可以让正在运行的协同程序挂起,必要的时候再恢复其运行,而且,一对yield和resume还可以互换数据,下面我们直接上代码:
function co()
	for i=1,10 do
		print(i)
		coroutine.yield()
	end
end
local coroutineTest = coroutine.create(co)
--执行一次后挂起
coroutine.resume(coroutineTest)
--接着上次执行后挂起
coroutine.resume(coroutineTest)
print("*************************************************")
--resume如果没有错误会返回true和yield中传入的所有参数
co1 = coroutine.create(function(a,b) coroutine.yield(a+b,a-b) end )
print(coroutine.resume(co1,1,2))
print("*************************************************")
--yield也会返回resume传给他的额外参数
co2 = coroutine.create(function() print(coroutine.yield())end)
coroutine.resume(co2) --不打印任何东西
coroutine.resume(co2,1,2) 
print("*************************************************")
--resume会返回主函数所有返回的值
co3 = coroutine.create(function(a,b)coroutine.yield(a+b) return 6 end)
print(coroutine.resume(co3,2,3))
print(coroutine.resume(co3))
下面是运行之后的结果:

2.协同程序中的经典案例

1.生产者消费者问题.
大体意思就是说生产者函数不停的生产,消费者函数不停的消费生产者的产品,为了不浪费资源,我们需要将这两个函数匹配起来,那么到底以谁作为循环条件的开始就成了要讨论的问题,协同程序可以很好的解决这类问题,我们直接lua程序设计一书中提供的代码:
--消费者
function consumer()
	while true do
		local x=receive()
		io.write(x,"\n")
	end
end

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

function send(x)
	--用到前面的yield 和 resume互传参数
	coroutine.yield(x)
end

--生产者
producer = coroutine.create(
	function()
		while true do 	
			local x = io.read()
			send(x)
		end
	end)


这种设计是以消费者来启动的,消费者需要值时唤醒生产者去生产,生产者生产后被挂起,我们还可以在当中对生产的值进行包装,然后供消费者消费,依然看书中提供的代码:
--消费者
function consumer(prod)
	while true do 
		local x =receive(prod) --获取新的值
		io.write(x,"\n")
	end	
end

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

function send(x)
	--用到前面的yield 和 resume互传参数
	coroutine.yield(x)
end

--生产者
function producer()
	return coroutine.create(
		function()
			while true do 	
				local x = io.read()
				send(x)
			end
		end)
end

function filter(prod)
	return coroutine.create(
		function()
			for line = 1,math.huge do
				local x = receive(prod)
				x=string.format("消费".."%5d行  %s",line,x)
				send(x)
			end
		end	)
end

--运行上述程序
consumer(filter(producer()))



运行上述程序的结果为:

如果出现乱码,请用记事本打开你的lua文件保存为asic格式在再试。
2.用协同实现迭代器
我在之前的博客中讲到过利用closure和for循环分别实现lua中的迭代器,但是不可避免的他们都需要保存上一次成功迭代的一些信息,而学习了协同之后,我们可以利用协同来实现不用保存迭代状态信息的迭代器。来看下面的实例:
function factory()
	local co = coroutine.create(
					function()
						for i=1,10 do 
							coroutine.yield(i)
						end
					end
				)
	return function() 
		local code ,res = coroutine.resume(co)
		return res
		end
end

for p in factory() do
	print("item=".. p)
end
注意,这里仅仅是为了演示协同实现的迭代器不用保存两次迭代之间的状态信息,其实我们完全可以用一个循环来实现该实例,但是当遇到一些很复杂的迭代时,使用协同实现的迭代器是很方便的,下面贴上运行结果:

对于上面的迭代器的实现,我们把对一个协同的创建和唤醒放在一个函数里,由于这样的做法在lua里很常见,lua提供了一个函数来简化流程:coroutine.wrap(),该函数会创建一个协同程序,但是不返回协同本身,而是返回一个函数,每次调用该函数就唤醒协同程序,于是我们可以把上面的factory改写成以下形式,运行后结果与上面是一样的:
function factory()
	return coroutine.wrap(function()for i=1,10 do coroutine.yield(i) end end)	
end
for p in factory() do
	print("item=".. p)
end
当然我们还可以使用协同实现http远程并发下载资源,就类似于多线程一样,但是少了多线程同步带来的一些问题,代价就是cpu资源会耗费的多,lua程序设计一书中有详细的例子,这里就不再实现了。
3.小结:
通过上面的讲述,我们认识到lua中的协同是一个很有用的东西,在我们的不同的处理下可以完成很多任务,上面讲了一点协同的基础,如果想更深刻的认识他,需要我们在平时多留意用到协同的场合,博主水平有限,如有不正确的地方欢迎大家批评指出,交流使人进步。




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值