📜  lua中重建的等待函数(1)

📅  最后修改于: 2023-12-03 15:02:49.040000             🧑  作者: Mango

Lua中重建的等待函数

在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()来计算时间,然后将时间控制在需要等待的时间内,就可以实现等待的效果。但是,这样的写法存在几个问题:

  • 代码不够简洁和直观。
  • 占用CPU资源,在需要等待较长时间时会浪费资源。

为了解决这个问题,我们可以使用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中使用等待函数,不妨试一试这个重建的等待函数。