multithreading Lua 协程到底有什么用?为什么这段代码不像我期望的那样工作?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/5128375/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
What are Lua coroutines even for? Why doesn't this code work as I expect it?
提问by kellpossible
I'm having trouble understanding this code... I was expecting something similar to threading where I would get an output with random "nooo" and "yaaaay"s interspersed with each other as they both do the printing asynchronously, but rather I discovered that the main thread seems to block on the first calling of coroutine.resume() and thus prevents the next from being started until the first has yielded.
我在理解这段代码时遇到问题......我期待类似于线程的东西,我会得到一个随机“nooo”和“yaaaay”的输出,因为它们都以异步方式进行打印,但我发现主线程似乎在第一次调用 coroutine.resume() 时阻塞,从而阻止下一个启动,直到第一个产生为止。
If this is the intended operation coroutines, what are they useful for, and how would I achieve the goal I was hoping for? Would I have to implement my own scheduler for these coroutines to operate asynchronously?, because that seems messy, and I may as well use functions!
如果这是预期的操作协程,它们有什么用处,我将如何实现我希望的目标?我是否必须为这些协程实现我自己的调度程序才能异步操作?因为这看起来很混乱,我还不如使用函数!
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)
回答by deft_code
Coroutines aren't threads.
协程不是线程。
Coroutines are like threads that are never actively scheduled. So yes you are kinda correct that you would have to write you own scheduler to have both coroutines run simultaneously.
协程就像从不主动调度的线程。因此,是的,您必须编写自己的调度程序才能让两个协程同时运行,这一点是正确的。
However you are missing the bigger picture when it comes to coroutines. Check out wikipedia's list of coroutine uses. Here is one concrete example that might guide you in the right direction.
然而,当涉及到协程时,你错过了更大的图景。查看维基百科的协程使用列表。这是一个具体的例子,可以指导您朝着正确的方向前进。
-- 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
The above script could be written to run iteratively with a switch statement and some clever state variables. But it is much more clear when written as a coroutine. The above script could be a thread but do you really need to dedicate a kernel thread to this simple code. A busy game level could have 100's of these coroutines running without impacting performance. However if each of these were a thread you mightget away with 20-30 before performance started to suffer.
可以编写上述脚本以使用 switch 语句和一些巧妙的状态变量迭代运行。但是当写成协程时就更清楚了。上面的脚本可能是一个线程,但您真的需要将内核线程专用于这个简单的代码。一个繁忙的游戏关卡可以运行 100 个这样的协程,而不会影响性能。但是,如果这些都是一个线程,您可能会在性能开始受到影响之前获得 20-30。
A coroutine is meant to allow me to write code that stores state on the stack so that I can stop running it for a while (the wait
functions) and start it again where I left off.
协程旨在允许我编写代码将状态存储在堆栈上,以便我可以停止运行它一段时间(wait
函数)并在我停止的地方重新启动它。
回答by Mankarse
Since there have been a number of comments asking how to implement the wait
function that would make deft_code
's example work, I've decided to write a possible implementation. The general idea is that we have a scheduler with a list of coroutines, and the scheduler decides when to return control to the coroutines after they give up control with their wait
calls. This is desirable because it makes asynchronous code be readable and easy to reason about.
由于有许多评论询问如何实现wait
使deft_code
的示例工作的函数,因此我决定编写一个可能的实现。总体思路是,我们有一个带有协程列表的调度程序,调度程序决定在协程放弃wait
调用控制权后何时将控制权返回给协程。这是可取的,因为它使异步代码可读且易于推理。
This is only one possible use of coroutines, they are a more general abstraction tool that can be used for many different purposes (such as writing iterators and generators, writing stateful stream processing objects (for example, multiple stages in a parser), implementing exceptions and continuations, etc.).
这只是协程的一种可能用途,它们是一种更通用的抽象工具,可以用于许多不同的目的(例如编写迭代器和生成器、编写有状态的流处理对象(例如,解析器中的多个阶段)、实现异常和延续等)。
First: the scheduler definition:
第一:调度器定义:
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
Now, initialising the world:
现在,初始化世界:
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))
The (dummy) interface to the game:
游戏的(虚拟)界面:
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
}
And the game loop:
和游戏循环:
while true do
scheduler.run(frame_number,game_control)
frame_number = frame_number+1
end
回答by dejf
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