看了一下《Programming in Lua》里的协程程序的运用,总觉得有点像雾里看花一样,捉不到重点,不知道怎么去运用,但在洗澡时灵光一闪,突然想明白了这不只是使用了Reactor(反应时同步时间分派)模式吗。在这里写篇博客跟大家分享一些拙见。
先贴一下之前看得不怎么懂的源码
function download (host, file) -->协同程序
local c = assert(socket.connect(host, 80))
local count = 0 -- counts number of bytes read
c:send("GET " .. file .. " HTTP/1.0\r\n\r\n")
while true do
local s, status = receive©
count = count + string.len(s)
if status == "closed" then break end
end
c:close()
print(file, count)
end
function receive (connection) -->我把它叫做"中断程序"
connection:timeout(0) --> do not block
local s, status = connection:receive(2^10)
if status == "timeout" then
coroutine.yield(connection)
end
return s, status
end
threads = {} --> list of all live threads
function get (host, file) --> 工厂函数
local co = coroutine.create(function ()
download(host, file)
end)
table.insert(threads, co) --> insert it in the list
end
function dispatcher () -->分派器函数
while true do
local n = table.getn(threads)
if n == 0 then break end
for i=1,n do
local status, res = coroutine.resume(threads[i])
if not res then
table.remove(threads, i)
break
end
end
end
end
首先来看一下
dispatcher函数实际上充当了Reactor模式中的select和poll/epoll
get这个工厂函数充当了向分派器注册“线程”(协同程序)的作用
download与receive这两个函数在Reactor模式*同起到一个线程的作用
不同点是:1、以上Lua代码中通过额外的恢复/挂起操作代替了Reactor模式中的分派器检测系统资源来确定线程是否活动
2、因为分派器与各“线程”(协同程序)的执行实际上都在一个线程中执行,不能利用系统在真正线程之间分时切换,必须手动实现切换。
Coroutine-协程模型的优点是:
1、切换上下文的代价比较小。
2、不需要考虑同步问题。
实际上按照Reactor模式使用多协程程序很简单
只需要实现一个全局的分派器,开放注册“线程”的接口。写需要的协同程序(必须包含一个中断条件),并注册到分派器。
运行分派器就可以实现简单的多线程并发。当然这只是一个最简单的模型,只能在当至少有一个“线程”有数据可读取的时候运行比较良好,如果各个“线程”都处于空闲状态,则分配器会不断轮询线程状态,导致消耗大量时间。