关于多线程:如何在C ++中实现线程安全引用计数

关于多线程:如何在C ++中实现线程安全引用计数

How to implement thread safe reference counting in C++

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

我总是遇到这样的问题:关键操作不是原子操作,并且可用的X86 Interlock操作不足以实现引用计数系统。

以下文章介绍了此主题,但需要特殊的CPU指令:

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


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

效果很好;没有大惊小怪,没有头脑。 shared_ptr <>类负责处理对引用计数的所有锁定。


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

1
2
3
4
5
do
   read the count
   perform mathematical operation
   interlockedcompareexchange( destination, updated count, old count)
until the interlockedcompareexchange returns the success code.

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


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

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


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

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


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

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

如果以下情况适用于您:

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

...那么实际的垃圾回收可能是一个更好的选择。尽管GC在性能方面声誉不佳,但这是相对的。我认为它与锁定智能指针相比非常有利。这是CLR团队选择真正的GC而不是使用引用计数的原因的重要组成部分。请参阅本文,特别是如果您一直在进行以下工作,则对参考分配的含义进行严格的比较:

没有引用计数:

1
a = b;

引用计数:

1
2
3
4
5
6
7
8
if (a != null)
    if (InterlockedDecrement(ref a.m_ref) == 0)
            a.FinalRelease();

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

a = b;

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

具体来说,如果您不能保证智能指针不会在分配给另一个智能指针的过程中发生变化,则说明您做错了或开始做的事情很不可靠。如果在将智能指针分配给另一个智能指针时可以更改它,则意味着进行分配的代码不拥有该智能指针,因此怀疑该指针始于此。


如果指令本身不是原子指令,则需要将更新相应变量的代码段设为关键段。

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

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

价格昂贵:可能吧。但是,对于所有需要保证的事情,都有代价。


推荐阅读