std::shared_ptr 和 std::experimental::atomic_shared_ptr 有什么区别?

新手上路,请多包涵

我阅读了 Antony Williams以下 文章,据我了解,除了 中的 std::experimental::atomic_shared_ptr std::shared_ptr 中的原子共享计数之外,指向共享对象的实际指针也是原子的吗?

但是,当我阅读 lock_free_stack 在 Antony 的关于 C++ 并发 的书中描述的参考计数版本时,在我看来,同样适用于 std::shared_ptr ,因为像 std::atomic_load 这样的功能 --- , std::atomic_compare_exchnage_weak 应用于 std::shared_ptr 的实例。

 template <class T>
class lock_free_stack
{
public:
  void push(const T& data)
  {
    const std::shared_ptr<node> new_node = std::make_shared<node>(data);
    new_node->next = std::atomic_load(&head_);
    while (!std::atomic_compare_exchange_weak(&head_, &new_node->next, new_node));
  }

  std::shared_ptr<T> pop()
  {
    std::shared_ptr<node> old_head = std::atomic_load(&head_);
    while(old_head &&
          !std::atomic_compare_exchange_weak(&head_, &old_head, old_head->next));
    return old_head ? old_head->data : std::shared_ptr<T>();
  }

private:
  struct node
  {
    std::shared_ptr<T> data;
    std::shared_ptr<node> next;

    node(const T& data_) : data(std::make_shared<T>(data_)) {}
  };

private:
  std::shared_ptr<node> head_;
};

这两种类型的智能指针之间的确切区别是什么,如果 std::shared_ptr 实例中的指针不是原子的,为什么上述无锁堆栈实现可能?

原文由 bobeff 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 1.5k
2 个回答

shared_ptr 中的原子“东西”不是共享指针本身,而是它指向的控制块。这意味着只要您不跨多个线程改变 shared_ptr ,就可以了。请注意, 复制 shared_ptr 只会改变控制块,而不是 shared_ptr 本身。

 std::shared_ptr<int> ptr = std::make_shared<int>(4);
for (auto i =0;i<10;i++){
   std::thread([ptr]{ auto copy = ptr; }).detach(); //ok, only mutates the control block
}

改变共享指针本身,例如从多个线程为其分配不同的值,是一种数据竞争,例如:

 std::shared_ptr<int> ptr = std::make_shared<int>(4);
std::thread threadA([&ptr]{
   ptr = std::make_shared<int>(10);
});
std::thread threadB([&ptr]{
   ptr = std::make_shared<int>(20);
});

在这里,我们通过使其指向来自多个线程的不同值来改变控制块(这没关系)以及共享指针本身。这不行。

该问题的一个解决方案是用锁包裹 shared_ptr ,但是这种解决方案在某些争用下没有那么大的可扩展性,并且在某种意义上失去了标准共享指针的自动感觉。

另一种解决方案是使用您引用的标准函数,例如 std::atomic_compare_exchange_weak 。这使得同步共享指针的工作成为我们不喜欢的手动工作。

这就是原子共享指针发挥作用的地方。您可以从多个线程中改变共享指针,而不必担心数据竞争,也无需使用任何锁。独立功能将是成员功能,它们的使用对用户来说会更加自然。这种指针对于无锁数据结构非常有用。

原文由 David Haim 发布,翻译遵循 CC BY-SA 3.0 许可协议

Calling std::atomic_load() or std::atomic_compare_exchange_weak() on a shared_ptr is functionally equivalent to calling atomic_shared_ptr::load() or atomic_shared_ptr::atomic_compare_exchange_weak() .两者之间应该没有任何性能差异。在 --- 上调用 std::atomic_load()std::atomic_compare_exchange_weak() atomic_shared_ptr 将在语法上是多余的,并且可能会或可能不会导致性能损失。

原文由 atb 发布,翻译遵循 CC BY-SA 3.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题