Lua学习笔记(三)
协程(coroutine):
协程与多线程的比较,有自己的堆栈、局部变量、指令指针等,但是协程本身与其他协程共享全局变量。主要不同在于,多处理器下,多线程可以真实的同时运行多个线程。而协程任意时刻只能有一个在真实运行,并且只有在明确要求被挂起时才会挂起。
协程有三个状态:挂起,运行,停止。
co = coroutine.create(function() … end) -- 刚创建时为挂起态返回一个thread coroutine.status(co) -- 返回suspended 检测协程的状态 为挂起 coroutine.resume(co) -- 运行 coroutine.status(co) -- 返回dead 已经结束了 协程使用yield来自行挂起,resume和yield可以相互交换数据。 co = coroutine.create(function(a,b) print(“fun",a,b) print("co",coroutine.yield(a+b,a*b,a-b)) return 1,2 end) coroutine.resume(co,5,6) -- fun 5 6 返回true,11,30,-1 coroutine.resume(co,3,4) -- co 3 4 返回true,1,2
调用 coroutine.resume 函数执行一个协程。 第一次调用 coroutine.resume 时,第一个参数应传入 coroutine.create 返回的thread对象,然后协程从其主函数的第一行开始执行。 传递给 coroutine.resume 的其他参数将作为协程主函数的参数传入。 协程启动之后,将一直运行到它终止或 让出。
协程的运行可能被两种方式终止: 正常途径是主函数返回 (显式返回或运行完最后一条指令); 非正常途径是发生了一个未被捕获的错误。 对于正常结束, coroutine.resume 将返回 true, 并接上协程主函数的返回值。 当错误发生时, coroutine.resume 将返回 false 与错误消息。
通过调用 coroutine.yield 使协程暂停执行,让出执行权。 协程让出时,对应的最近 coroutine.resume 函数会立刻返回,即使该让出操作发生在内嵌函数调用中 (即不在主函数,但在主函数直接或间接调用的函数内部)。 在协程让出的情况下, coroutine.resume 也会返回 true, 并加上传给 coroutine.yield 的参数。 当下次重启同一个协程时, 协程会接着从让出点继续执行。 调用coroutine.yield 会返回任何传给 coroutine.resume 的第一个参数之外的其他参数。
与 coroutine.create 类似, coroutine.wrap 函数也会创建一个协程。 不同之处在于,它不返回协程本身,而是返回一个函数。 调用这个函数将启动该协程。 传递给该函数的任何参数均当作 coroutine.resume 的额外参数。 coroutine.wrap 返回 coroutine.resume 的所有返回值,除了第一个返回值(布尔型的错误码)。 和 coroutine.resume 不同,coroutine.wrap 不会捕获错误; 而是将任何错误都传播给调用者。
管道过滤器:
function receive (prod) local status, value = coroutine.resume(prod) return value end function send (x) coroutine.yield(x) end function producer () return coroutine.create(function () while true do local x = io.read() send(x) end end) end -- produce new value function filter (prod) return coroutine.create(function () local line = 1 while true do local x = receive(prod) -- get new value x = string.format("%5d %s", line, x) send(x) -- send it to consumer line = line + 1 end end) end function consumer (prod) while true do local x = receive(prod) -- get new value io.write(x, "\n") -- consume new value end end
在生产者消费者中间加了一个filter用来添加行号。消费者主导,需要消费时resume filter,filter再resume producer获取商品,处理过后返回给consumer,自己yield。
迭代器协程:
打印一个数组的全排列
function permgen(a, n) if n == 0 then coroutine.yield(a) else a[n],a[i]= a[i],a[n] --交换第i个和最后一个 permgen(a,n-1) a[n],a[i]=a[i],a[n] -- 换回来 end end function perm(a) local n = #a local co = coroutine.create(function() permgen(a,n) end) return function() — iterator local code,res = coroutine.resume(co) — 到n=0的时候会yield 返回给resume 此时res就是排列的一个解 return res — 最后一次resume以后返回nil end end -- perm返回一个迭代器函数,并不需要提供状态变量和控制变量。之后迭代器函数每次产生控制变量p,p即为一个排列。printResult打印p的所有元素 for p in perm({1,2,3,4}) do printResult(p) end
非抢占式多线程:
多个协同程序执行任务时,可能任务中间很多时间花在不需要抢占的操作比如等待时,可以用统一调度的方式,在一个协程发现自己不需要抢占资源的时候可以主动yield出来,resume其他协程执行。这样保证不用把排队时间浪费在不必要的等待中。