【问题标题】:What's the proper way to use coroutines for event handling?使用协程进行事件处理的正确方法是什么?
【发布时间】:2012-07-22 06:59:21
【问题描述】:

我试图弄清楚如何使用协程(在 Lua 中)处理事件。我看到一种常见的方法似乎是创建产生当前协程的包装函数,然后在您等待的事情发生时恢复它。这似乎是一个不错的解决方案,但是这些问题呢? :

  1. 如何同时等待多个事件,并根据先发生的事件进行分支?还是应该重新设计程序以避免这种情况?

  2. 如何取消等待一定时间?事件循环可以在其套接字发送/接收包装器中包含超时参数,但是自定义事件呢?

  3. 如何触发协程从外部改变其状态?例如,我想要一个在调用时会导致协程跳转到不同步骤或开始等待不同事件的函数。

编辑:

目前我有一个系统,我在其中注册一个带有事件的协程,并且每次事件发生时,协程都会以事件名称和信息作为参数恢复。使用这个系统,1 和 2 不是问题,3 可以通过让 coro 期望一个特殊的事件名称来解决,使其跳转到不同的步骤,并使用该名称作为 arg 恢复它。同样,自定义对象也可以具有注册事件处理程序的方法。

我只是想知道这是否被认为是使用协程进行事件处理的正确方法。例如,如果我有一个读取事件和一个计时器事件(作为读取的超时),并且读取事件首先发生,我必须手动取消计时器。它似乎不适合顺序性质或使用协程处理事件。

【问题讨论】:

  • “我只是想知道这是否被认为是使用协程进行事件处理的正确方法。”如果它对你有用,那么它就是正确的方法。为什么要使用计时器事件来超时另一个事件?简单地将超时内置到您的事件系统中不是更有意义吗?
  • “如果它适合你,那么它就是正确的方法”它可以正常工作(但不是很好),我在想可能有一种更好的方法,但我错过了这是因为我对协程不是非常熟悉。 “你为什么要使用计时器事件来超时另一个事件?”。我可以,但是自定义事件源都必须实现超时。也许我可以通过添加一个系统来向主事件循环注册自定义事件源来解决这个问题?最后,也许我应该在第一次触发时自动删除处理程序?

标签: events lua coroutine


【解决方案1】:

你如何同时等待多个事件,并根据哪个事件先发生分支?

如果你需要为此使用协程,而不仅仅是你注册的 Lua 函数(例如,如果你有一个函数来做一些事情,等待一个事件,然后做更多的事情),那么这很简单.当协程恢复时,coroutine.yield 将返回传递给coroutine.resume 的所有值。

所以只需传递事件,让脚本自行决定是否等待它。实际上,您可以构建一个简单的函数来执行此操作:

function WaitForEvents(...)
  local events = {...}
  assert(#... ~= 0, "You must pass at least one parameter")

  do
    RegisterForAnyEvent(coroutine.running()) --Registers the coroutine with the system, so that it will be resumed when an event is fired.
    local event = coroutine.yield()
    for i, testEvt in ipairs(events) do
      if(event == testEvt) then
        return
      end
    end
  until(false)
end

这个函数将继续产生直到它给出的事件之一被触发。该循环假定RegisterForAnyEvent 是临时的,只为一个事件注册函数,因此每次触发事件时都需要重新注册。

如何取消等待一定时间?

在上面的循环中放一个计数器,一定时间后离开。我将把它作为练习留给读者;这完全取决于您的应用程序如何测量时间。

如何从外部触发协程改变其状态?

您不能将 Lua 函数魔术到不同的“状态”。您只能调用函数并让它们返回结果。所以如果你想在某个进程中跳过,你必须编写你的 Lua 函数系统才能跳过。

如何做到这一点取决于您。你可以让每组非等待命令成为一个单独的 Lua 函数。或者你可以设计你的等待状态来跳过。或者其他。

【讨论】:

  • 我现在就是这样做的,但我看到大多数示例(和库,如 Copas)使用产生包装器来阻塞函数而不是事件注册函数,然后手动调用 @987654325 @。我的问题是如何使用 that 类型的协程事件系统解决我的问题,因为据我所知,它看起来更优雅并且是“正确”的方式(如果不是,请纠正我)。
  • @mtk358:然后你把这段代码放在包装器中。这不是火箭科学。如果您有等待多个事件的东西,那么任何“生成包装器”都应该具有等待多个事件的代码。
  • 我知道“yielding wrappers”一次只能等待一个事件,但我问也许应该避免这种情况并以不同的方式编写(但如何?)。让我对此感到疑惑的主要是 Copas 使用“yielding wrappers”,并且似乎完全忽略了我的三个问题。但它仍然必须以某种方式可用,对吧?
  • @mtk358:这个 Copas 库的文档是否说您可以等待多个事件?如果没有,那么它根本不允许(或者您必须自己编写代码)。另外,我从来没有说过你不能使用 yielding 包装器来等待多个事件。确实,我说的正好相反。您只需让您的包装器获得多个事件,确切地如上所示。事实上,这 一个屈服的包装器。
猜你喜欢
  • 2012-03-31
  • 1970-01-01
  • 1970-01-01
  • 2021-09-12
  • 1970-01-01
  • 2013-07-24
  • 1970-01-01
  • 2010-11-04
  • 1970-01-01
相关资源
最近更新 更多