控制流

lua coroutine 的使用场景

  • 异步回调转同步:

假设有如下的异步调用的lua代码

function(arg)
  async(arg,function(state,ret)
      if state=="case1" then
        process_case1(ret)
      elseif state=="case2" then
        process_case2(ret)
      end
  end)
end

可以通过lua的coroutine转成同步写法

- lua
coroutine.wrap(function(arg)   
   await_async(arg)

   local error,value = coroutine.yield()
   if value.state=="case1" then
      process_case1(value.ret)
      error,value = coroutine.yield()
   end

   if value.state=="case2" then
      process_case2(value.ret)
   else
      assert(false)
   end
   coroutine.yield()

end)

function await_async(arg)
   local self = coroutine.running()
   async(arg,function(state,ret)   
      if state=="case1" then
        coroutine.resume(self,{state,ret})
      elseif state=="case2" then
        coroutine.resume(self,{state,ret})
      end
   end)
end

看似写了更多代码,不过如果在合理的封装下,可以避免callback hell。

实际上,yield/resume的使用,和c里面使用信号量有点类似

local value = nil

async(funciton(state,ret)
    value = {state,ret}
    if state=="case1" then
      event.singal()
    elseif state=="case2" then
      event.singal()
    end
end)

event.waitonce()
if value.state=="case1" then
  process_case1()
  event.waitonece()
end

if value.state=="case2" then
  process_case2()
else
  assert(false)
end

当然,这里的event.waitonce()会把整个线程卡死,这个代码需要跑在单独的线程上。
这也是event.waitonce()和coroutine.yiled()的区别的地方,如果上fiber的话,则是一样的。

  • 动画更新

一个动画引擎内部有一个死循环,不停的刷帧,在关键时间点上调用外部注入的不同时间点的update方法。
如果不用coroutine的情况下,这样写:

function update(time)
  switch(time)
    case time1:load() update1() save() break
    case time2:load() update2() save() break
    case time3:load() update3() save() break
    defulat: break
  end
end
animat.add(update)

但是,如果updatei之间有很多依赖状态,这些中间状态如果都用load(),save()等保存的话,会逐渐变的繁杂。
换用coroutine之后可以是:

local co = coroutine.wrap(function(time)
  local error = nil
  if time==time1 then
    update1()
    error,time = coroutine.yield()
  end 

  if time==time2 then
    update2()
    error,time = coroutine.yield()
  end 
  
  if time==time3 then
    update3()
    error,time = coroutine.yield()
  end 

end)

function update(time)
  coroutine.resume(co,time)
end

animat.add(update)

调度状态机

假设有如下队列

function Queue:Push(arg) end
function Queue:Pop() end
function Queue:Size() end

假设有一个异步消费队列的调用,我们可以做异步链式调用:

local queue = ...
function shedule()
  local item = queue:Pop()
  if item then
    dosomethinig_async(item,function()
      --// 在回调里异步调度下一个
      async(function()
        shedule() 
      end)
    end)
  end
end

但我们也可以用状态机调度

local queue = ...
function shedule()
  --// 定时刷帧,如果窗口不为0,就批量调度一部分item
  start_timer(function()
    local cc = congestion_controller
    if cc:current_window()>0 then
      while cc.dec_winidow() do
        local item = queue:Pop()
        --// 回调里只要更新cc状态即可
        dosomethinig_async(item,function() 
          cc.update() 
        end)
      end
    end
  end,1000)
end

你可能感兴趣的:(控制流)