📅  最后修改于: 2023-12-03 15:02:49.040000             🧑  作者: Mango
在Lua中,有一个很常用的函数是coroutine.yield()
,这个函数可以让当前协程挂起,并将控制权交给父协程或主程序。然而,这个函数有一个缺点,就是需要手动计算出挂起的时间,如下所示:
function sleep(seconds)
local start = os.clock()
while os.clock() - start < seconds do end
end
print("start")
sleep(2)
print("end")
上面的代码中,我们定义了一个sleep
函数,使用os.clock()
来计算时间,然后将时间控制在需要等待的时间内,就可以实现等待的效果。但是,这样的写法存在几个问题:
为了解决这个问题,我们可以使用socket
库中的socket.sleep()
函数,这个函数同样是挂起当前协程,但是只需要传递等待的时间作为参数,就可以实现等待的效果,如下所示:
local socket = require("socket")
print("start")
socket.sleep(2)
print("end")
这样的写法代码很简洁,等待的时间也很准确。但是,socket
库并不是Lua的标准库,需要手动安装,并且对于其他平台的兼容性也不一定良好。
为了将等待函数更好地整合到Lua中,我们可以利用Lua的元表技术和coroutine.yield()
函数,实现一个通用的等待函数。代码如下所示:
local m = {
__call = function(self, seconds)
local co = coroutine.running()
if co then
self.timers[co] = os.clock() + seconds
coroutine.yield()
else
self:busy_wait(seconds)
end
end,
busy_wait = function(self, seconds)
local start = os.clock()
while os.clock() - start < seconds do end
end,
timers = setmetatable({}, { __mode = "kv" })
}
function m:update()
local now = os.clock()
for co, time in pairs(self.timers) do
if now >= time then
self.timers[co] = nil
coroutine.resume(co)
end
end
end
setmetatable(m, { __index = m.timers })
return m
上面的代码中,我们定义了一个叫做wait
的函数。这个函数可以直接调用,并将需要等待的时间作为参数传递进去。函数内部先判断当前是否在协程中,如果是,就将当前时间加上等待时间,存储到timers
表中,然后使用coroutine.yield()
函数挂起当前协程。如果没有在协程中,就会使用较繁琐的busy_wait
函数实现等待。
同时,我们还定义了timers
表,用于存储需要等待的协程和对应的等待时间。这个表的元表中,使用弱引用的方式防止内存泄漏。
最后,我们还定义了一个update
函数,用于定时地检查等待时间是否到期,如果到期了,就使用coroutine.resume()
函数恢复挂起的协程。
使用这个等待函数非常简单,只需要调用wait
函数,传入需要等待的时间即可,如下所示:
local wait = require("wait")
print("start")
wait(2)
print("end")
上面的代码中,我们先在程序顶部引入了wait
模块,然后在需要等待的地方调用wait
函数,传入需要等待的时间。这样,程序就会在等待时间到期后,继续往下执行。
这个等待函数的好处是代码简洁,使用方便,并且可以与协程无缝结合,不会浪费CPU资源。如果你需要在Lua中使用等待函数,不妨试一试这个重建的等待函数。