7

实际上,我正在使用 Visual C++ 尝试将 lua 函数绑定为套接字事件的回调(在另一个线程中)。我在一个线程中初始化 lua 的东西,而套接字在另一个线程中,所以每次套接字发送/接收消息时,它都会调用 lua 函数,而 lua 函数根据里面的“标签”确定它应该做什么信息。

所以我的问题是:

  1. 由于我将相同的 Lua 状态传递给 lua 函数,这安全吗?它不需要某种保护吗?lua 函数是从另一个 thead 调用的,所以我猜它们可能会同时被调用。

  2. 如果不安全,这种情况的解决方案是什么?

4

3 回答 3

2

异步回调到 Lua 状态是不安全的。

有很多方法可以处理这个问题。最受欢迎的涉及某种投票。

最近的通用同步库是DarkSideSync

一个流行的 Lua 绑定到 libev 是lua-ev

这个 SO 答案推荐使用 LuaSocket 的 Lua Lanes。

于 2013-05-16T03:04:34.080 回答
1
  1. 在多个线程中同时调用一个 Lua 状态中的函数是不安全的。

  2. 我正在处理同样的问题,因为在我的应用程序中,通信等所有基础知识都由 C++ 处理,所有业务逻辑都在 Lua 中实现。我所做的是创建一个Lua 状态池,这些状态都是增量创建和初始化的(一旦没有足够的状态,创建一个并使用通用函数/对象进行初始化)。它是这样工作的:

    • 一旦连接线程需要调用 Lua 函数,它会检查 Lua 状态的实例,在单独的(代理)全局表中初始化特定的全局变量(我称之为线程/连接上下文),以防止污染原始全局,但由原始全局索引
    • 调用 Lua 函数
    • 将 Lua 状态检查回池中,在池中将其恢复为“就绪”状态(处置代理全局表)

我认为这种方法也非常适合您的情况。池在上次签出时检查每个状态(以间隔为基础)。当时间差足够大时,它会破坏状态以保存资源并根据当前服务器负载调整活动状态的数量。签出的状态是可用状态中最近使用的状态。

在实现这样的池时,您需要考虑一些事项:

  • 每个状态都需要填充相同的变量和全局函数,这会增加内存消耗。
  • 实现池中状态计数的上限
  • 确保每个状态中的所有全局变量都处于一致状态,如果它们发生变化(这里我建议只预填充静态全局变量,同时在签出状态时填充动态全局变量)
  • 动态加载函数。在我的例子中,有成千上万个可以在 Lua 中调用的函数/过程。让它们在所有州不断加载将是一种巨大的浪费。因此,我将它们保留在 C++ 端编译的字节码,并在需要时加载它们。事实证明,在我的情况下不会对性能产生太大影响,但您的里程可能会有所不同。要记住的一件事是只加载一次。假设您调用一个脚本,该脚本需要在循环中调用另一个动态加载的函数。然后,您应该在循环之前将该函数加载为本地一次。否则会极大地影响性能。

当然,这只是一种想法,但事实证明它最适合我。

于 2013-05-16T07:59:51.547 回答
1
  1. 正如其他人所说,这不安全
  2. 取决于您的用例

最简单的解决方案是使用lua_locklua_unlock宏使用全局锁。那将使用单个 Lua 状态,由单个互斥锁锁定。对于少量的回调它可能就足够了,但对于更高的流量它可能不会因为产生的开销。

一旦你需要更好的性能,WB 提到的 Lua 状态池是处理这个问题的好方法。这里最棘手的部分是我发现跨多个状态同步全局数据。

Doug 提到的DarkSideSync在主应用程序循环驻留在 Lua 端的情况下很有用。我为此专门写了它。在您的情况下,这似乎不合适。话说回来; 根据您的需要,您可能会考虑更改您的应用程序,以便主循环确实驻留在 Lua 端。如果你只处理套接字,那么你可以使用 LuaSocket 并且根本不需要同步。但显然这取决于应用程序还能做什么。

于 2013-05-16T11:40:57.067 回答