正确的迭代处理对象
昨天在写一个 AOI 模块,设计时又碰到一个对象迭代的老问题,必须谨慎对待,文以记之。
缘起:
当对象 A 进入 B 的 AOI 区域时,会触发一个 Enter 事件。这个事件处理是以回调函数的形式完成,如果回调函数中再次调用 AOI 模块,产生一次间接递归,就有可能破坏 AOI 模块内部的某些迭代过程。
更要命的是,如果回调函数内删除了一些相关对象,很有可能引起对已释放对象的访问错误。
这类问题在各种对象管理的程序框架中经常出现。除了上面提到的 AOI 模块,在 GUI 模块设计中也极其常见。下面谈谈我的解决方案吧。
首先,由于回调函数内部逻辑的不可预知性,我们一定要把实际的处理放在每个 API 实现的末尾。一旦真正的处理在中间,因为间接递归的可能性,极有可能保存在模块内部的上下文信息被其破坏。
正确的做法是在模块环境中创建一个唯一消息处理队列,把可能引发回调的消息都暂入到队列。由于队列只有 enter 和 leave 两个方法可以改变内部状态,迭代处理队列本身是不会出问题的。
另一个必须考虑的对象的释放时机。当我们用 C 或 C++ 这类没有 gc 机制的语言实现的时候,它是个相当头痛的问题。
因为,任何一个消息处理的回调函数都可能删除某些对象。而这些对象的指针极有可能放在消息队列中。不要跟我说在消息队列中放对象的智能指针,然后每个用到该对象的地方都使用智能指针访问。那样我会疯掉的,尤其是需要把回调函数这样的接口暴露给最终用户时,我可不希望在接口上暴露一个难看的智能指针类。即使拐弯没角的把接口问题解决掉,额外的性能付出也让人心里不大舒服。
解决方法有两个:
消息里不记对象的指针,而用一个进程生命期唯一 id 标识。 再用一张 hash 表做映射。对象删除后,id 不再找的到对应的对象。这个方案在上次的一篇文章 的留言中提过。
用一个间接指针。对象删除后把间接指针置一个标记。再次调用时就可以知道对象被删除了。间接指针本身占用的空间从额外的备用池里分配。定期回收,和真正的删除垃圾对象。
个人比较推荐性价比更高的第 2 方案。接下来展开谈一下细节。
所谓定期回收,应当隐藏起来让用户不可见。我们可以把回收过程放在新对象创建的时候,因为这个时候恰巧需要新的内存资源,是释放旧资源的最好时机。由于对象创建也可能发生在消息处理的过程中,我们不能在消息处理期间做这个工作。所以垃圾收集的时候必须检查消息队列为空,才可以开始。
回收分两个步骤:
一是删除垃圾对象,这个去检查间接指针中的删除标记位就可以了。
二是删除间接指针本身。当消息队列为空时,完全可以把所有间接指针整个一起拿掉。
以上问题考虑周全后,基本上万无一失了。:D
Comments
Posted by: 尉迟方 | (28) September 25, 2007 04:17 PM
Posted by: Zwinger | (27) September 21, 2007 12:52 PM
Posted by: zetorchen | (26) September 21, 2007 11:34 AM
Posted by: zetorchen | (25) September 21, 2007 11:32 AM
Posted by: nothanks | (24) September 21, 2007 11:21 AM
Posted by: david | (23) September 21, 2007 10:52 AM
Posted by: dfk | (22) September 20, 2007 04:16 PM
Posted by: dfk | (21) September 20, 2007 04:15 PM
Posted by: Atry | (20) September 20, 2007 11:02 AM
Posted by: Cloud | (19) September 20, 2007 12:49 AM
Posted by: Atry | (18) September 20, 2007 12:30 AM
Posted by: Cloud | (17) September 19, 2007 11:52 PM
Posted by: Atry | (16) September 19, 2007 11:17 PM
Posted by: Atry | (15) September 19, 2007 11:08 PM
Posted by: Atry | (14) September 19, 2007 11:06 PM
Posted by: Cloud | (13) September 19, 2007 10:14 PM
Posted by: Atry | (12) September 19, 2007 09:42 PM
Posted by: Atry | (11) September 19, 2007 09:34 PM
Posted by: Cloud | (10) September 19, 2007 07:03 PM
Posted by: Atry | (9) September 19, 2007 04:55 PM
Posted by: Atry | (8) September 19, 2007 04:39 PM
Posted by: Cloud | (7) September 19, 2007 03:09 PM
Posted by: Atry | (6) September 19, 2007 01:21 PM
Posted by: Atry | (5) September 19, 2007 01:14 PM
Posted by: Cloud | (4) September 19, 2007 01:05 PM
Posted by: nothanks | (3) September 19, 2007 01:00 PM
Posted by: Anonymous | (2) September 19, 2007 12:28 PM
Posted by: Atry | (1) September 19, 2007 12:02 PM