第9章 协同程序(coroutine)
协同程序与线程(thread)差不多,也就是一条执行序列,拥有自己独立的栈、局部变量和指令指针,同时又与其他协同程序共享全局变量和其他大部分东西。主要区别:一个具有多个线程的程序可以同时运行多个线程,而协同程序却需要彼此协作地运行。就是说一个具有多个协同程序的程序在任意时刻只能运行一个协同程序,并且正在运行的协同程序只会在其显示的地要求挂起(suspend)时,它的执行才会暂停。
9.1 协同程序基础
Lua将所有关于协同程序的函数放置在一个名为“coroutine”的table中。函数create用于创建新的协同程序,它只有一个参数,就是一个函数。该函数的代码就是协同程序所需执行的内容。
co = coroutine.create(function() print(“hi”) end)
print (co) –>thread: 003BFE98
协同程序的4种不同的状态:挂起(suspended)、运行(running)、死亡(dead)、和正常(normal)。当创建一个协同程序时,它处于挂起状态。可以通过函数status来检查协同程序的状态:
print(coroutine.status(co)) –>suspended
函数coroutine.resume用于启动或再次启动一个协同程序的执行,并将其状态由挂起状态改为运行:
coroutine.resume(co) –>hi
函数yield可以让一个运行中的协同程序挂起,而之后可以再次恢复它的运行。
co = coroutine.create( function()
for i=1, 10 do
print(“co “, i)
coroutine.yield()
end
end)
coroutine.resume(co) –> co 1
print(coroutine.status(co)) –> suspended
coroutine.resume(co) –> co 2
Lua的协同程序还具有一项有用的机制,就是可以通过一对resume-yield来交换数据。在第一次调用resume时,并没有对应的yield在等待它,因此所有传递给resume的额外参数都将视为协同程序主函数的参数:
co = coroutine.create( function(a, b, c)
print(“co”, a, b, c)
end)
coroutine.resume(co, 1, 2, 3) – coroutine.resume(co) 1,2,3为参数
Lua提供的一种“非对称的协同程序(asymmetric coroutine)”。也就是说,Lua提供了两个函数来控制协同程序的执行,一个用于挂起,另一个用于恢复执行。
9.2 管道(pipe)与过滤器(filter)
过滤器(filter)是一种位于生产者和消费者之间的处理功能,可用于对数据的一些交换。过滤器既是一个消费者又是生产者,它唤醒一个生产者促使其产生新值,然后又将变换后的值传递给消费者。
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 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 function consumer (prod) while true do local x = receive(prod) -- 获取新值 io.write(x, "\n") -- 消费新值 end end consumer(filter(producer()))
9.3 以协同程序实现迭代器
function permgen (a, n) n = n or #a -- 默认n为a的大小 if n <= 1 then -- 还需要改变吗? printResult(a) else for i=1, n do -- 将第i个元素放到数组末尾 a[n], a[i] = a[i], a[n] permgen(a, n - 1) -- 生成其余元素的排列 a[n], a[i] = a[i], a[n] -- 恢复第i个元素 end end end function printResult(a) for i = 1, #a do io.write(a[i], " ") end io.write("\n") end permgen({1,2,3,4})
将上面的printResult改为yield:
function premgen(a, n) n = n or #a if n <= 1 then coroutine.yield(a) else for i=1, n do -- 将第i个元素放到数组末尾 a[n], a[i] = a[i], a[n] permgen(a, n - 1) -- 生成其余元素的排列 a[n], a[i] = a[i], a[n] -- 恢复第i个元素 end end end function permutations(a) local co = coroutine.create( function () permgen(a) end) return function() -- 迭代器 local code, res = coroutine.resume(co) return res end end function printResult(d) for i = 1, #d do io.write(d[i], " ") end io.write("\n") end for p in permutations {"a", "b", "c"} do printResult(p) end
9.4 非抢占式的多线程
协同程序是非抢占式的。当一个协同程序运行时,是无法从外部停止它的。只有当协同程序显示地要求挂起时(调用yield),它才会停止。
第10章 完整的示例
10.1 数据描述
略。
10.2 马尔可夫链算法
略.