如何在C++中实现线程安全引用计数
-
01-07-2019 - |
题
你如何实施 高效且线程安全的引用计数系统 在 X86 CPU 上使用 C++ 编程语言?
我总是遇到这样的问题 关键操作不是原子的, ,并且可用的 X86 互锁操作不足以实现引用计数系统。
以下文章涵盖了该主题,但需要特殊的 CPU 指令:
解决方案
现在,您可以使用 Boost/TR1 shared_ptr<> 智能指针来保存引用计数引用。
效果很好;没有大惊小怪,没有混乱。shared_ptr<> 类负责引用计数所需的所有锁定。
其他提示
在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 指令使用适当的内部函数。
严格来说,您需要等到 C++0x 才能用纯 C++ 编写线程安全代码。
现在,您可以使用 Posix,或者围绕比较和交换和/或互锁增量/减量创建您自己的独立于平台的包装器。
Win32 InterlockedIncrementAcquire 和 InterlockedDecrementRelease (如果你想安全并关心可能重新排序的平台,因此你需要同时发出内存屏障)或 InterlockedIncrement 和 InterlockedDecrement (如果你确定你将保持 x86),是原子的并且将做这份工作。
也就是说,Boost/TR1 shared_ptr<> 将为您处理所有这些,因此除非您需要自己实现它,否则您可能会尽力坚持它。
请记住,锁定的成本非常高,每次在智能指针之间传递对象时都会发生这种情况 - 即使该对象当前由一个线程拥有(智能指针库不知道这一点)。
鉴于此,这里可能有一条适用的经验法则(我很高兴得到纠正!)
如果以下情况适用于您:
- 您拥有复杂的数据结构,很难为其编写析构函数(或者根据设计,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;
如果指令本身不是原子的,那么您需要将更新适当变量的代码部分设置为关键部分。
IE。 您需要使用某种锁定方案来防止其他线程进入该代码部分。当然,锁必须是原子的,但您可以在 pthread_mutex 类中找到原子锁定机制。
效率问题:pthread 库尽可能高效,并且仍然保证互斥锁对于操作系统来说是原子的。
是不是很贵:大概。但凡是需要保证的事情都是有成本的。
ddj 文章中发布的特定代码增加了额外的复杂性,以解决使用智能指针时出现的错误。
具体来说,如果您不能保证智能指针在分配给另一个智能指针时不会发生变化,那么您就做错了,或者一开始就做了一些非常不可靠的事情。如果智能指针在分配给另一个智能指针时可以更改,则意味着执行分配的代码不拥有该智能指针,这从一开始就值得怀疑。