【问题标题】:Is calling a lua function(as a callback) from another thread safe enough?从另一个线程调用 lua 函数(作为回调)是否足够安全?
【发布时间】:2013-05-10 19:06:09
【问题描述】:

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

所以我的问题是:

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

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

【问题讨论】:

    标签: visual-c++ lua


    【解决方案1】:

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

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

    最近的通用同步库是DarkSideSync

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

    This SO answer 推荐使用 LuaSocket 的 Lua Lanes。

    【讨论】:

    • 您能解释一下 Luan Lanes 和 DarkSideSync 之间的区别吗? DarkSideSync 的例子很少,我不知道如何使用 DarkSideSync
    • Lanes 提供了一种数据类型 Lindas,它可以在 Lanes、异步 Lua 状态之间安全共享。 DarkSideSync 更专注于 Lua 和异步 C 库之间的通知和通信。
    • 因此,如果我在 Lua 脚本中使用 C 库中的互斥锁封装了一个回调,那么从 C 中定期调用它是不安全的?
    【解决方案2】:
    1. 在一个 Lua 状态下同时在多个线程中调用函数是不安全的。

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

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

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

    在实施此类池时需要考虑一些事项:

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

    当然,这只是一个想法,但结果证明最适合我。

    【讨论】:

    • 听起来不错。但是如果可能的话,是否有任何代码开源/共享?
    • @MickeyShine - 我不能给你任何与 Lua 相关的东西,但你可以看看mysql++ 连接池的实现,它的工作方式或多或少是相同的。 Connection Pool
    • 如何通过将调用包装在互斥锁中以同步方式从不同线程调用 Lua 状态?那安全吗?
    【解决方案3】:
    1. 正如其他人提到的那样,它不安全
    2. 取决于您的用例

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

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

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

    【讨论】:

      猜你喜欢
      • 2011-07-14
      • 1970-01-01
      • 2018-06-30
      • 2016-12-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-12-17
      相关资源
      最近更新 更多