我听说互联网上有关于能够在Lua中热交换代码的麻烦,类似于在Java,Erlang,Lisp等中完成的代码。但是,30分钟的谷歌搜索没有发现任何问题。有没有人读过这方面的实质内容?有没有经验呢?它在LuaJIT中工作还是仅在参考VM中工作?
我对这项技术作为开发/调试的快捷方式比对实时环境中的升级路径更感兴趣。
答案 0 :(得分:19)
Lua和大多数脚本语言都不支持定义它时最常用的“热交换”形式。也就是说,你不能保证更改磁盘上的文件并对其进行任何更改,将其自身传播到正在执行的程序中。
然而,Lua和大多数脚本语言完全能够实现受控形式的热交换。全球职能是全球职能。模块只是加载全局函数(如果你以这种方式使用它们)。因此,如果模块加载全局函数,则可以在模块更改时再次重新加载模块,并且这些全局函数引用将更改为新加载的函数。
然而,Lua和大多数脚本语言都不能保证这一点。所有这一切都在发生全球状态数据的变化。如果有人将旧函数复制到局部变量中,他们仍然可以访问它。如果模块使用本地状态数据,则新版本的模块无法访问旧模块的状态。如果模块创建某种具有成员函数的对象,除非从全局变量中获取这些成员,否则这些对象将始终引用旧函数,而不是新函数。等等。
另外,Lua不是线程安全的;您不能在某个时刻中断lua_State
并尝试再次加载模块。因此,您必须设置一些特定的时间点,以便检查输出并重新加载已更改的文件。
所以你可以做到,但从它可能发生的意义上来说它并不是“支持”的。你必须为它工作,你必须要小心你如何写东西以及你在本地和全局函数中的用途。
答案 1 :(得分:0)
作为尼科尔said,该语言本身并不能为您做到这一点。
但是,如果您想自己实现类似的功能,就不那么困难了,唯一“预防”您的是任何“剩余”引用(仍然指向旧代码)和事实require
将其返回值缓存在package.loaded
中。
我要做的方法是将您的代码分为3个模块:
main.lua
)的重新加载逻辑data.lua
)payload.lua
),请确保您不保留对此的任何引用(例如,当您必须向某些库提供回调时,这是不可能的;请参见下文)。-- main.lua:
local PL = require("payload")
local D = require("data")
function reload(module)
package.loaded[module]=nil -- this makes `require` forget about its cache
return require(module)
end
PL.setX(5)
PL.setY(10)
PL.printX()
PL.printY()
-- .... somehow detect you want to reload:
print "reloading"
PL = reload("payload") -- make sure you don't keep references to PL elsewhere, e.g. as a function upvalue!
PL.printX()
PL.printY()
-- data.lua:
return {} -- this is a pretty dumb module, it's literally just a table stored in `package.loaded.data` to make sure everyone gets the same instance when requiring it.
-- payload.lua:
local D = require("data")
local y = 0
return {
setX = function(nx) D.x = nx end, -- using the data module is preserved
setY = function(ny) y = ny end, -- using a local is reset upon reload
printX = function() print("x:",D.x) end,
printY = function() print("y:", y) end
}
输出:
x: 5
y: 10
reloading
x: 5
y: 0
您可以通过拥有一个“注册表模块”来完善该逻辑,该“注册表模块”可以为您跟踪所有需求/重新加载,并抽象出对模块的所有访问(从而允许您替换引用),并使用该注册表上的__index
元表,您可以使其变得非常透明,而不必在各处调用丑陋的吸气剂。这也意味着您可以提供“一个班轮”回调,然后,如果任何第三方库需要的话,实际上只是通过注册表进行尾调用。
答案 2 :(得分:-1)
完全同意Nicol。我正在使用火炬(机器学习的框架) - 它建立在lua之上。我不得不使用相同的main.lua
启动多个实验。由于不明确支持热交换,我所要做的就是更改main.lua
中的一些参数并使用相同的命令启动实验。这确保了我不必制作代码的多个副本,并且之前的实验不受此更改的影响。