【问题标题】:atomic_load/atomic_store on std::shared_ptr in VC11 - why the global spinlock?VC11 中 std::shared_ptr 上的 atomic_load/atomic_store - 为什么是全局自旋锁?
【发布时间】:2013-03-09 11:22:09
【问题描述】:

我试图准确了解如何使用原子操作安全地管理共享指针。原来 VC11 (Visual Studio 2012) 支持 C++11,因此可以允许 std::shared_ptr 上的读/写竞争。 我想检查我是否理解了基础知识,然后询问有关 VC11 中 std::shared_ptr 上原子操作的实现细节。

std::shared_ptr<A> x, y, z;
x = std::make_shared<A>(args1);
y = std::make_shared<A>(args2);

线程 1

std::shared_ptr<A> temp = std::atomic_load(y);

线程 2

std::atomic_store(&y, z);

如果没有原子,竞争可能会导致temp 最终处于损坏状态,或者线程 2 可能会删除原始 y 指向的 A 实例,就像线程 1 试图复制和添加 shared_ptr 一样,这会让它指向一个“僵尸”对象。

关于 VC11 中 atomic_load 和 atomic_store 的问题:

我注意到他们使用自旋锁对全局变量执行测试和设置。 所以我想知道:为什么不在 shared_ptr 本身的引用计数器的最高位上进行测试和设置呢?这样不同shared_ptr上的锁就不会相互竞争。没有这样做是有原因的吗?

编辑:atomic_is_lock_free 的 VS 实现。不足为奇,因为它对一切都使用自旋锁。仍然想知道为什么他们不能让它使用 shared_ptr-instance-specific 锁而不是全局锁。

template <class _Ty> inline
bool atomic_is_lock_free(const shared_ptr<_Ty> *)
{   // return true if atomic operations on shared_ptr<_Ty> are lock-free
    return (false);
}

【问题讨论】:

  • 您是否检查过 std::atomic_is_lock_free(&some_shared_ptr) 在您的平台上是否为真。
  • 我在某处读到 shared_ptr 在内部使用原子操作。也许您不需要将它们混合在一起。我说的对吗?
  • shared_ptr 使用原子操作来增加和减少其使用计数。如果没有这些原子操作,即使读取/读取竞争(即 2 个线程从同一个 shared_ptr 分配到一个本地线程),引用计数器的值也可能无效 - 如果不是,在某些排序下使用计数可能会增加不到 2原子更新。但仅此一项并不能解决我在帖子中提到的 2 个竞争条件;这是一个单独的问题。

标签: c++ visual-studio-2012 shared-ptr atomic


【解决方案1】:

您不能对 shared_ptr 的引用计数进行原子测试和设置,因为引用计数存储在 shared_ptr 的控制块中。当你开始尝试测试和设置时,另一个线程可能已经释放了最后一个 shared_ptr 引用并从你下面删除了控制块。

Thread 1                                  Thread 2
Read control block address

                                          Decrement ref count (now 0)
                                          Delete control block

Test-and-set ref count (undefined behaviour)

请记住,这里的前提是多个线程正在操作 same shared_ptr 实例。如果每个线程都有自己的实例(指向同一个受控对象),那么我们就没有问题,也不需要原子的 shared_ptr 操作。

【讨论】:

  • 你的断言是错误的,只有当你的代码有错误时它才是正确的。两个线程都在访问 shared_ptr 并且第一个线程正在调用 atomic_load 。它应该能够依赖于这样一个事实,即在该调用期间,对象的引用计数至少为 1。如果其他线程删除该对象,例如通过在其中调用 reset,则不能是这种情况.但这是一个错误,因为它只允许使用 atomic_store 或 atomic_exchange 操作 shared_ptr。
  • 如果我的回答不清楚,我很抱歉,但它是正确的。考虑我们有一个全局 shared_ptr、P 和两个线程都在操作它的情况。线程 1 尝试制作副本 (shared_ptr a = P),线程 2 尝试替换该值 (P = b)。这可能会导致我描述的场景。
  • 如果你想访问一个共享的 shared_ptr ,你需要使用互斥锁或使用 atomic_load 和 atomic_store 其他一切都会引入竞争条件。 (很好的解释:youtu.be/lkgszkPnV8g?t=17m13s
  • 我很清楚,Rioki。我认为你在这里错过了重点。最初的问题是关于 atomic_shared_ptr 的潜在实现。我在这里试图解释的是为什么即使使用原子加载和存储,该实现也不起作用。
【解决方案2】:

修改引用计数的最高位需要将引用计数作为计数器处理的代码以忽略该最高位。也就是说,它会使最常见的使用速度变慢,以便在不太常见的情况下提供轻微的速度提升。

【讨论】:

猜你喜欢
  • 1970-01-01
  • 2017-10-29
  • 1970-01-01
  • 2010-12-29
  • 1970-01-01
  • 2017-06-23
  • 2022-07-26
  • 2011-08-20
相关资源
最近更新 更多