环境表

lua-users home
wiki

Lua 5.0

在 Lua 5.0.2 中,Lua 闭包(但不是 C 闭包)具有环境表。环境表用于查找未绑定的名称(即“全局变量”)。当通过执行函数表达式或语句创建新的闭包时,新闭包会获取当前执行闭包的环境表,因此在没有更改的情况下,所有闭包共享同一个环境表,这使得它几乎与以前版本的 Lua 中的全局表相同。

可以使用 lua_getfenv()(在 C 中)和 getfenv()(在 Lua 中)访问闭包的环境表;可以使用 lua_setfenv() / setfenv() 将其设置为不同的表。有关这些函数的信息,请参阅手册。

在 5.0.2 中,C 闭包实际上共享一个全局表,该表与当前执行的线程相关联。C 闭包可以使用伪索引 LUA_GLOBALSINDEX 访问此表(实际上可以使用 lua_replace(L, LUA_GLOBALSINDEX) 修改它)。这很有用,但可能有点混乱。

Lua 5.1

在 5.1 中,每个 C 闭包都有自己的环境表引用。但是,LUA_GLOBALSINDEX 伪索引没有改变,因此引用 LUA_GLOBALSINDEX 的现有代码仍然会引用当前执行线程的“全局”表。(该表现在被称为线程的环境表,可以使用应用于线程对象本身的 lua_getfenv 和 lua_setfenv 访问和修改它。)为了访问闭包的环境表,可以使用 LUA_ENVIRONINDEX;可以使用 lua_getfenv()lua_setfenv()(应用于闭包对象)访问和修改闭包环境表(对于 C 闭包或 Lua 闭包)。

您可以使用 lua_replace(L, LUA_ENVIRONINDEX) 替换“您自己的”环境,就像您可以使用 lua_replace(L, LUA_GLOBALSINDEX) 更改全局表一样。但是,更常见的情况是在新创建的闭包上调用 lua_setfenv()

(完整)用户数据也有环境表,尽管正如我在其他地方论证的那样(UserDataRefinement),这个名称令人困惑。用户数据环境表用于存储与用户数据相关的的信息;它们没有伪索引。

新创建的闭包(和用户数据)会从当前正在执行的闭包(如果有)获取其环境表,否则从当前线程获取。(再说一次,正如我在其他地方论证的那样,这对用户数据来说不是一个特别有用的默认值,但我不会再赘述。)

把什么放在哪里?

C 闭包并没有真正意义上的绑定变量和非绑定变量的概念。但是,C 闭包确实有一些地方可以存放 Lua 值

弄清楚这些选项中的哪一个适合什么,可能是一个挑战,但我提供我自己的非常个人化的指南

使用 upvalue

使用闭包自己的环境表

使用线程的环境表

使用注册表

关于线程安全的说明

唯一线程安全的选项是堆栈和线程的环境表。如果lua_locklua_unlock被正确定义,你无法通过存储到注册表或闭包的上值数组或环境表来破坏 Lua 状态,但没有任何东西可以阻止另一个(操作系统)线程同时存储其他东西,从而导致标准的竞争条件,其中访问和更新被另一个进程中断。特别是,这适用于 lauxlib 的引用实现(如果存储在注册表中)。然而,有了所有可用的环境表,现在应该更容易避免这种情况。

线程的环境表是线程安全的,前提是每个 Lua 线程都映射到单个操作系统线程(它可以是多对一映射);也就是说,给定的 Lua 线程始终在同一个操作系统线程中运行。这使得它成为保存 lauxlib 风格引用的一个有吸引力的选项,如果无法完全避免使用它们。

闭包的上值数组和环境表在相同条件下是线程安全的,但这通常更难保证。对于短生命周期闭包(例如,返回给实现迭代器的闭包)来说,这很可能是这种情况,但对于长生命周期闭包(例如,库函数)来说,这不太可能。

如果你发现自己在多线程环境中修改注册表,你应该认真考虑用某种形式的锁来保护修改。这也适用于使用基于注册表的lauxlib功能,例如luaL_newmetatable(),如果它的使用不限于线程初始化之前的阶段。


最近更改 · 偏好设置
编辑 · 历史记录
最后编辑于 2005 年 12 月 22 日凌晨 2:18 GMT (差异)