浅谈Lua的Coroutine-协程的多"线程"并发模型

时间:2021-06-08 20:03:51

看了一下《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模式使用多协程程序很简单

  只需要实现一个全局的分派器,开放注册“线程”的接口。写需要的协同程序(必须包含一个中断条件),并注册到分派器。

  运行分派器就可以实现简单的多线程并发。当然这只是一个最简单的模型,只能在当至少有一个“线程”有数据可读取的时候运行比较良好,如果各个“线程”都处于空闲状态,则分配器会不断轮询线程状态,导致消耗大量时间。