2011-02-26 142 views
26

我在理解这段代码时遇到了麻烦......我期待着类似于线程的东西,在那里我会得到一个随机的“nooo”和“yaaaay”的输出,因为它们都是异步执行打印的,而是我发现主线程似乎阻止了coroutine.resume()的第一次调用,从而阻止下一个被启动,直到第一个已经产生。甚至为什么Lua协程?为什么这个代码不能像我期望的那样工作?

如果这是计划的操作协程,它们对于什么有用,以及如何实现我期望的目标?我是否必须实现自己的调度程序才能使这些协同程序异步操作?因为这看起来很麻烦,我不妨使用函数!

co1 = coroutine.create(function() 
     local i = 1 
     while i < 200 do 
       print("nooo") 
       i = i + 1 
     end 
     coroutine.yield() 
end) 

co2 = coroutine.create(function() 
     local i = 1 
     while i < 200 do 
       print("yaaaay") 
       i = i + 1 
     end 
     coroutine.yield() 
end) 

coroutine.resume(co1) 
coroutine.resume(co2) 
+6

协程不是线程。它们是线程的,因为它们是独立的执行环境,但是你的程序是调度程序,并且它们不是抢占式的。我会添加一个完整的答案,但我的Lua是生锈的,所以我不能提供任何代码示例... – 2011-02-26 17:09:26

回答

46

协程不是线程。

协程就像永远不会被有效调度的线程。所以是的,你是不错的,你必须写你自己的调度程序,使两个协同程序同时运行。

但是,当涉及到协程时,您错过了更大的想法。查看维基百科的list of coroutine uses。这里有一个具体的例子可以指导你朝着正确的方向发展。

-- level script 
-- a volcano erupts every 2 minutes 
function level_with_volcano(interface) 

    while true do 
     wait(seconds(5)) 
     start_eruption_volcano() 
     wait(frames(10)) 
     s = play("rumble_sound") 
     wait(end_of(s)) 
     start_camera_shake() 

     -- more stuff 

     wait(minutes(2)) 
    end 


end 

上面的脚本可以编写成与switch语句和一些聪明的状态变量迭代运行。但作为协同程序编写时,它更加清晰。上面的脚本可能是一个线程,但是你真的需要将一个内核线程专用于这个简单的代码。繁忙的游戏级别可能会有100个这样的协同程序运行,而不会影响性能。但是,如果这些线程中的每一个都是可能在与20-30之间脱节,然后性能开始受到影响。

协程意味着允许我编写在堆栈中存储状态的代码,这样我可以停止运行一段时间(wait函数),并在我离开的地方再次启动它。

+1

谢谢,这很好地回答了我的问题! – kellpossible 2011-02-26 18:06:58

+0

完美。我想知道如何在LUA中做到这一点,现在我知道要搜索什么:)。谢谢。 – dcousens 2011-06-28 03:13:01

+1

@deft为了帮助澄清,上述示例如何工作?据我所知,lua有3个主要功能来支持协程:“创建”,“恢复”和“屈服”。 afaik'wait'不是内置的lua函数。那么等待是否由你执行?收益发生在哪里?在最后一个yield之后* x *时间过后,你如何确保这个协程'resume'? – greatwolf 2011-08-26 21:51:53

9
co1 = coroutine.create(
    function() 
     for i = 1, 100 do 
      print("co1_"..i) 
      coroutine.yield(co2) 
     end 
    end 
) 

co2 = coroutine.create(
    function() 
     for i = 1, 100 do 
      print("co2_"..i) 
      coroutine.yield(co1) 
     end 
    end 
) 

for i = 1, 100 do 
    coroutine.resume(co1) 
    coroutine.resume(co2) 
end 
8

既然已经出现了一些意见,询问如何实现wait功能,这将使deft_code的例子的工作,我决定写一个可能的实现。总的想法是我们有一个调度程序和一系列协程,调度程序决定何时在控制wait调用后放弃对协程的控制。这是可取的,因为它使得异步代码易读并易于推理。

这只是协程的一种可能的用法,它们是一个更一般的抽象工具,可用于许多不同的目的(例如编写迭代器和生成器,编写有状态的流处理对象(例如,解析器),实现异常和延续等)。

第一:调度定义:

local function make_scheduler() 
    local script_container = {} 
    return { 
     continue_script = function(frame, script_thread) 
      if script_container[frame] == nil then 
       script_container[frame] = {} 
      end 
      table.insert(script_container[frame],script_thread) 
     end, 
     run = function(frame_number, game_control) 
      if script_container[frame_number] ~= nil then 
       local i = 1 
       --recheck length every time, to allow coroutine to resume on 
       --the same frame 
       local scripts = script_container[frame_number] 
       while i <= #scripts do 
        local success, msg = 
         coroutine.resume(scripts[i], game_control) 
        if not success then error(msg) end 
        i = i + 1 
       end 
      end 
     end 
    } 
end 

现在,初始化世界:

local fps = 60 
local frame_number = 1 
local scheduler = make_scheduler() 

scheduler.continue_script(frame_number, coroutine.create(function(game_control) 
    while true do 
     --instead of passing game_control as a parameter, we could 
     --have equivalently put these values in _ENV. 
     game_control.wait(game_control.seconds(5)) 
     game_control.start_eruption_volcano() 
     game_control.wait(game_control.frames(10)) 
     s = game_control.play("rumble_sound") 
     game_control.wait(game_control.end_of(s)) 
     game_control.start_camera_shake() 

     -- more stuff 

     game_control.wait(game_control.minutes(2)) 
    end 
end)) 

的(虚拟)接口的游戏:

local game_control = { 
    seconds = function(num) 
     return math.floor(num*fps) 
    end, 
    minutes = function(num) 
     return math.floor(num*fps*60) 
    end, 
    frames = function(num) return num end, 
    end_of = function(sound) 
     return sound.start+sound.duration-frame_number 
    end, 
    wait = function(frames_to_wait_for) 
     scheduler.continue_script(
      frame_number+math.floor(frames_to_wait_for), 
      coroutine.running()) 
     coroutine.yield() 
    end, 
    start_eruption_volcano = function() 
     --obviously in a real game, this could 
     --affect some datastructure in a non-immediate way 
     print(frame_number..": The volcano is erupting, BOOM!") 
    end, 
    start_camera_shake = function() 
     print(frame_number..": SHAKY!") 
    end, 
    play = function(soundname) 
     print(frame_number..": Playing: "..soundname) 
     return {name = soundname, start = frame_number, duration = 30} 
    end 
} 

而且游戏圈:

while true do 
    scheduler.run(frame_number,game_control) 
    frame_number = frame_number+1 
end 
相关问题