Lua setfenv на темы не работает
Я хочу загрузить некоторые функции в состоянии lua, а затем иметь возможность вызывать функции из потоков lua. я пытаюсь setfenv
в потоках, чтобы создаваемые ими переменные были ограничены потоками и не появлялись в глобальной среде.
lua_State *L = luaL_newstate();
luaL_openlibs(L);
dostring(L, "function f1() my_var = 100 print('var set') end");/* create func on state */
/* ^-- a wrapper which does loadstring + pcall with error handling */
lua_State *l1 = lua_newthread(L);
lua_pushthread(l1); /* l1: t */
lua_newtable(l1); /* l1: t T1{} */
lua_newtable(l1); /* l1: t T1{} T2{} */
lua_getglobal(l1, "_G"); /* l1: t T1{} T2{} _G */
lua_setfield(l1, -2, "__index"); /* l1: t T1{} T2{} ( T2.__index = _G) */
lua_setmetatable(l1, -2); /* l1: t T1 ( T1{}.mt = T2 ) */
if (!lua_setfenv(l1, -2)) /* l1: t (t.fenv = T1) */
printf("setfenv fail!\n");
lua_pop(l1, 1);
dostring(l1, "print('l1: ', my_var)"); /* --> nil (expected) */
dostring(l1, "f1() print('l1: ', my_var)"); /* --> l1: 100 (ok) */
dostring(L, "print('L: ', my_var)"); /* --> L: 100 (No!) */
Я что-то здесь не так делаю? (Я не хочу загружать функцию в потоках, потому что их может быть много, и загрузка их один раз в состояние кажется правильным подходом)
--Редактировать--
Решение, кажется, заключается в следующем:
- создать новую таблицу env для каждого потока (с
__index = _G
) - для каждой функции, которая работает внутри него, выполните
setfenv(f1, getfenv(0))
1 ответ
Каждая функция имеет свой собственный фенвен. f1
Фенвен _G
, поэтому при вызове (независимо от того, в каком потоке он вызывается), он устанавливает глобальную переменную в _G
, Одним из вариантов является явная ссылка на среду потока из f1
например
function f1()
local env = getfenv(0)
env.my_var = 100
print('var set')
end
Другой способ - предоставить каждому потоку личную копию f1
,
Третий вариант - создать прокси-fenv (один и тот же для всех потоков и функций) с __index
а также __newindex
метаметоды, которые делегируют текущей среде потока (т.е. getfenv(0)
.):
-- Step 1: Create the shared proxy object that delegates to the
-- current thread environment.
local tlproxy = {} -- Always empty
local tlproxy_mt = {}
function tlproxy_mt:__index(k)
return getfenv(0)[k]
end
function tlproxy_mt:__newindex(k, v)
getfenv(0)[k] = v
end
setmetatable(tlproxy, tlproxy_mt)
-- Step 2: Give each new thread a new, empty environment table.
local tenv_mt = {}
tenv_mt.__index = _G -- allows access to _G.math etc.
local function createThread(f)
local thread = coroutine.create(f)
-- These functions will not work as expected if called from the new thread,
-- so disable them.
local tenv = {
load=false, loadfile=false, loadstring=false,
module=false, require=false
}
setmetatable(tenv, tenv_mt)
debug.setfenv(thread, tenv)
return thread
end
-- Step 3: When a function should use thread-local variables, it should be
-- given 'tlproxy' as its fenv.
function f1()
my_var = 0
while true do
my_var = my_var + 1
coroutine.yield(my_var)
end
end
setfenv(f1, tlproxy)
local c1 = createThread(f1)
local c2 = createThread(f1)
-- Output should be 1, 1, 2, 2...
-- Without thread-locals it would be 1, 2, 3, 4...
for _ = 1, 100 do
print(coroutine.resume(c1))
print(coroutine.resume(c2))
end
52,1 Bot