假设您在共享内存中有一个引用计数对象。引用计数表示使用对象的进程数,进程负责通过原子指令递增和递减计数,因此引用计数本身也在共享内存中(它可以是对象的一个字段,或者对象可能包含指向计数的指针,如果他们有助于解决这个问题,我愿意接受建议)。有时,一个进程会有一个错误,阻止它减少计数。如何尽可能轻松地找出哪个进程没有递减计数?
我想到的一个解决方案是给每个进程一个 UID(也许是它们的 PID)。然后当进程递减时,它们将它们的 UID 推送到与引用计数一起存储的链表(我选择链表是因为您可以使用 CAS 原子地追加到头部)。当你想要调试时,你有一个特殊的进程来查看共享内存中仍然存在的对象的链接列表,并且任何应用程序的 UID 不在列表中的都是尚未减少计数的应用程序。
此解决方案的缺点是它的内存使用量为 O(N),其中 N 是进程数。如果使用共享内存区域的进程数量很大,并且您有大量对象,那么这很快就会变得非常昂贵。我怀疑可能有一个半途而废的解决方案,其中使用部分固定大小的信息,您可以通过某种方式缩小可能进程列表的范围来协助调试,即使您无法查明单个进程也是如此。或者,如果您可以在只有一个进程没有减少时检测到哪个进程没有减少(即无法处理检测到 2 个或更多进程未能减少计数),这可能仍然会有很大帮助。
(这个问题有更多的“人性化”解决方案,比如确保所有应用程序都使用相同的库来访问共享内存区域,但是如果共享区域被视为二进制接口(interface)并且并非所有进程都将被由您编写的不受您控制的应用程序。此外,即使所有应用程序都使用相同的库,一个应用程序也可能在库外有一个错误,以这种方式破坏内存,从而阻止减少计数。是的,我正在使用不安全的语言,如 C/C++ ;)
编辑:在单进程情况下,您将拥有控制权,因此您可以使用 RAII (在 C++ 中)。
最佳答案
您可以为每个对象仅使用一个额外的整数来做到这一点。
将整数初始化为零。当进程增加对象的引用计数时,它会将其 PID 异或为整数:
object.tracker ^= self.pid;
当进程递减引用计数时,它会做同样的事情。
如果引用计数始终为 1,则跟踪器整数将等于增加它但没有减少它的进程的 PID。
这是有效的,因为异或是可交换的( (A ^ B) ^ C
== A ^ (B ^ C)
),所以如果一个进程对跟踪器进行异或它自己的 PID 偶数次,这与将它与 PID ^ PID
进行异或运算相同 - 为零,这使跟踪器值不受影响。
您也可以使用无符号值(定义为换行而不是溢出)- 在使用计数递增时加上 PID,在递减时减去它。
关于algorithm - 如何有效地调试共享内存中的引用计数问题?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2220108/