19

如何使用 C++ 编程语言在 X86 CPU 上实现高效且线程安全的引用计数系统?

我总是遇到关键操作不是原子的问题,可用的 X86 互锁操作不足以实现引用计数系统。

以下文章涵盖了该主题,但需要特殊的 CPU 指令:

http://www.ddj.com/architect/184401888

4

7 回答 7

11

现在,您可以使用 Boost/TR1 shared_ptr<> 智能指针来保持您的引用计数引用。

效果很好;没有大惊小怪,没有混乱。shared_ptr<> 类负责 refcount 所需的所有锁定。

于 2008-09-18T14:37:07.697 回答
5

在 VC++ 中,您可以使用_InterlockedCompareExchange

do
   read the count
   perform mathematical operation
   interlockedcompareexchange( destination, updated count, old count)
until the interlockedcompareexchange returns the success code.

在其他平台/编译器上,为 MS 的 _InterlockedCompareExchange 公开的 LOCK CMPXCHG 指令使用适当的内在函数。

于 2008-09-18T14:38:25.523 回答
3

严格来说,您需要等到 C++0x 才能使用纯 C++ 编写线程安全代码。

目前,您可以使用 Posix,或者围绕比较和交换和/或互锁递增/递减创建自己的平台独立包装器。

于 2008-09-18T14:38:06.930 回答
2

Win32 InterlockedIncrementAcquire 和 InterlockedDecrementRelease(如果您想安全并关心可能重新排序的平台,因此您需要同时发出内存屏障)或 InterlockedIncrement 和 InterlockedDecrement(如果您确定将保留 x86),是原子的并且将做这项工作。

也就是说,Boost/TR1 shared_ptr<> 将为您处理所有这些,因此除非您需要自己实现它,否则您可能会尽力坚持下去。

于 2008-12-08T11:04:33.000 回答
1

请记住,锁定非常昂贵,并且每次您在智能指针之间传递对象时都会发生这种情况 - 即使对象当前由一个线程拥有(智能指针库不知道这一点)。

鉴于此,这里可能有一个适用的经验法则(我很高兴得到纠正!)

如果以下情况适用于您:

  • 您有复杂的数据结构,很难为其编写析构函数(或者 STL 样式的值语义在设计上不合适),因此您需要智能指针来为您完成它,并且
  • 您正在使用共享这些对象的多个线程,并且
  • 您关心性能和正确性

...那么实际的垃圾收集可能是更好的选择。尽管 GC 在性能方面名声不佳,但这都是相对的。我相信它与锁定智能指针相比非常有利。这是 CLR 团队选择真正的 GC 而不是使用引用计数的重要原因。请参阅这篇文章,特别是如果您继续计算引用分配意味着什么的鲜明比较:

没有参考计数:

a = b;

参考计数:

if (a != null)
    if (InterlockedDecrement(ref a.m_ref) == 0)
            a.FinalRelease();

if (b != null)
    InterlockedIncrement(ref b.m_ref);

a = b;
于 2008-12-08T11:38:15.410 回答
0

如果指令本身不是原子的,那么您需要将更新相应变量的代码部分设为临界区。

,您需要通过使用某种锁定方案来防止其他线程进入该代码段。当然,锁需要是原子的,但是您可以在 pthread_mutex 类中找到原子锁定机制。

效率问题:pthread 库尽可能高效,并且仍然保证互斥锁对于您的操作系统是原子的。

贵吗:可能。但是对于需要保证的一切,都是有代价的。

于 2008-09-18T14:47:35.470 回答
0

该 ddj 文章中发布的特定代码增加了额外的复杂性,以解决使用智能指针的错误。

具体来说,如果你不能保证智能指针在分配给另一个智能指针时不会改变,那么你做错了,或者一开始做的事情非常不可靠。如果智能指针在分配给另一个智能指针时可以更改,这意味着执行分配的代码不拥有智能指针,这很可疑。

于 2008-09-23T22:44:28.707 回答