【问题标题】:Lua setfenv on threads doesnt seem to work线程上的 Lua setfenv 似乎不起作用
【发布时间】:2013-07-10 05:56:06
【问题描述】:

我想在 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!) */

我在这里做错了吗? (我不想在线程上加载函数,因为它们可能很多,并且 在状态上加载一次似乎是正确的方法)

--编辑--

解决方案,似乎是:

  • 为每个线程创建一个新的环境表(使用__index = _G
  • 对于其中运行的每个函数,执行setfenv(f1, getfenv(0))

【问题讨论】:

  • 你使用的 Lua 版本是什么?
  • 我正在使用 LuaJIT-2.0.2(即 Lua5.1 + AFAIK 的一些扩展)

标签: c lua environment-variables lua-5.1


【解决方案1】:

每个函数都有自己的 fenv。 f1的fenv是_G,所以调用的时候(不管在哪个线程调用),都会设置_G中的全局变量。 一种选择是从f1 显式引用线程环境,例如

function f1()
  local env = getfenv(0)
  env.my_var = 100
  print('var set')
end

另一个是给每个线程一个f1的私有副本。

第三种选择是使用__index__newindex 委托给当前线程环境的元方法(即getfenv(0))创建一个代理 fenv(所有线程和函数都相同):

-- 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

【讨论】:

  • 所以从你的解释来看,IIUC,我不能只在线程上setfenv,而是在调用之前为 每个函数 这样做 - 这是正确的吗? (我不想采用这种方法的原因是,因为可以有很多功能)。有没有办法伪造 set(通过元表或其他方式),以便在线程 env 中设置的变量不会在全局 env 中设置?
  • @vyom:是的,我想您可以拥有一个代理环境,其中包含引用当前线程环境的__index__newindex 元方法。这样你就不需要修改函数了。不过,我预计性能成本会很高。
  • 我会试试的。为线程作为一个整体(包括所有运行其中的函数)设置一个环境会很好,因为现在我必须做2个步骤,关联fenv 与线程,并将相同的fenv 与在其中运行的函数相关联!。正如您所提到的,成本更高(就性能和时间而言!)
  • 如果使用元方法,只需要设置一次函数 fenv。
  • 每次我调用一个函数时,我都需要设置它的 fenv(到它正在运行的线程的那个 - 因为它可以从多个线程中调用)。我不明白使用元方法如何避免这种情况
猜你喜欢
  • 1970-01-01
  • 2019-11-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-09-13
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多