2011-02-26 131 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 
相關問題