【问题标题】:Lua weak referencesLua 弱引用
【发布时间】:2011-09-17 00:55:07
【问题描述】:

我正在 Lua 中进行一个项目,我将在其中创建表并将它们存储在主表中,以便稍后删除。我会将这些表的引用传递给其他同级表。

master = {}
table.insert(master, {name = 'hello'})
table.insert(master, {name = 'world', pre = master[1]})

出现的问题是,当我希望从主表中删除引用时,该引用仍然保留在 master[2] 中。显然,我的第一个解决方案是让表格具有弱值。 (通过元表上的 .__mode,此处未显示)

只要我永远不会在这些表中存储单独引用的表,这行得通,而且会行得通。

table.insert(master, {name = 'goodbye', pre = master[2], some_table = {123}})

第三个元素 some_table 最终会被收集,因为这些表的值很弱,并且此表 (some_table) 不会在其他任何地方引用。这是不受欢迎的行为。我最新的解决方案涉及为主表中的表创建“弱引用对象”。一个简单的实现如下:

function WeakRef(t)
    r = {__mode = 'v', __index = t, __newindex = t}
    setmetatable(r, r)
    return r
end

这些弱引用对象的行为类似于boost::weak_ptrs 并实现了我的目标,但我不确定它们是否是我问题的最佳解决方案。

有没有更好的方法;更优雅的解决方案? 我的设计是否需要这个主表,可能有缺陷?

【问题讨论】:

  • "出现的问题是,当我希望从主表中删除引用时,引用仍然保留在 master[2] 中。"我很好奇为什么这是一个问题。是否需要通知其他代码真的 从主表中删除对象?因为 GC 不保证收集这些东西的时间范围。在 Lua 中需要如此严格的内存控制的设计到底是什么?
  • 他们本身不一定需要立即通知。预期设计是在模拟中指示物理对象的主阵列,当不再需要该对象时,可以将其移除。它类似于在一个数组中拥有一组强指针,然后围绕指向对象的弱指针展开。我的问题是我给对象提供了对其他对象的引用,如果我希望立即从模拟中删除一个对象,这可以阻止集合。这有意义吗?
  • 我明白你在做什么。我不确定为什么你这样做。既然弱引用不能保证立即消失,那么它们是否消失又有什么关系呢?那是我不明白的部分:为什么其他引用需要弱?弱引用不会防止意外使用死对象,因为 GC 可能还没有收集到它。它们变弱的唯一原因是为了防止循环依赖(Lua 没有问题)或者更有效地管理内存。您是否分配了足够的数据以致内存管理成为问题?
  • 我没有分配足够的数据,这在这一点上是个问题。我担心的是更少的记忆和更多的有效性。就像如果我想说“这个对象已经死了”,那么我希望它被删除并且对它的任何引用都为零。如果这个物体在下一次扫荡之前一直悬在某处未被收集,对我来说是可以的;一旦我宣布它“死亡”,我就不想再引用它了。

标签: garbage-collection lua weak-references


【解决方案1】:

鉴于:

  1. 您希望 master 成为您定义对象是否存在的“一个地方”
  2. 您的对象之间可以有链接

那么可能最简单的架构是将每个对象的成员之一保留为负责管理对其他对象的引用的“中间人”。步骤如下:

  1. 使master 成为普通表(不弱)
  2. 在每个物理对象上,创建一个名为 links 的弱表(或任何更适合您逻辑的名称)
  3. 使所有links 表变弱。使用它们来存储对其他对象的引用。

这是一个可能的实现。我已经在 Lua 5.1 中尝试过:

local function newWeakTable()
  return setmetatable({}, {__mode = "v"})
end

local master = {}

-- create two physical objects
local obj1 = { name = "obj1", links = newWeakTable() }
local obj2 = { name = "obj2", links = newWeakTable() }

-- link them
obj2.links.pre = obj1

-- insert them into master
table.insert(master, obj1)
table.insert(master, obj2)

-- master has 2 objects, and they are linked
assert(#master == 2)
assert(obj2.links.pre == obj1)

-- remove obj1 from master, and remove the variable reference
table.remove(master, 1)
obj1 = nil

-- run gc manually
collectgarbage("collect")

-- master has only 1 object now, and the link has dissapeared
assert(#master == 1)
assert(obj2.links.pre == nil)

print("Everything went as expected")

【讨论】:

  • 不会删除对要删除的表的引用需要遍历master 中的每个表,然后在这些表的表中递归搜索(如果有)?不过,让master 变弱听起来很有趣。
  • 如果您尝试删除“master[1]”,您应该已经有一个“master[2]”变量。毕竟,它们是相关的。
  • 它们确实是相关的,但是如果我在master 中有数百个对象,我该怎么办?我应该用另一个引用循环跟踪每个引用吗?通常,如果我在 C++ 中编程,我会做的是拥有一个强指针容器并将弱指针分发给那些请求它们的人。这就是我想知道的,是否可以在 Lua 中执行类似的操作,或者是否有特定于 Lua 的方式来执行此操作。似乎没有“强表”的概念。我在想我可以将 __valid 键设置为 true,然后在需要清理时将其设置为 false。
  • 我最后一条评论的字符数用完了,但我想说的是,我能想出的所有这些解决问题的想法在理论上可能有效,但它们会增加工作量脚本编写者必须这样做,以确保他们的所有引用都是有效的。我正在寻找其中最好的解决方案。
  • 您必须对数据和代码进行结构化,以便如果 master[2] 引用了 master[1],则 master[1] 必须存在。如果 master[1] 不再重要,并且您不必“告诉”master[2],那么它首先不应该在 master[2] 中。
猜你喜欢
  • 2013-10-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多