我阅读了 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 许可协议
shared_ptr
中的原子“东西”不是共享指针本身,而是它指向的控制块。这意味着只要您不跨多个线程改变shared_ptr
,就可以了。请注意, 复制shared_ptr
只会改变控制块,而不是shared_ptr
本身。改变共享指针本身,例如从多个线程为其分配不同的值,是一种数据竞争,例如:
在这里,我们通过使其指向来自多个线程的不同值来改变控制块(这没关系)以及共享指针本身。这不行。
该问题的一个解决方案是用锁包裹
shared_ptr
,但是这种解决方案在某些争用下没有那么大的可扩展性,并且在某种意义上失去了标准共享指针的自动感觉。另一种解决方案是使用您引用的标准函数,例如
std::atomic_compare_exchange_weak
。这使得同步共享指针的工作成为我们不喜欢的手动工作。这就是原子共享指针发挥作用的地方。您可以从多个线程中改变共享指针,而不必担心数据竞争,也无需使用任何锁。独立功能将是成员功能,它们的使用对用户来说会更加自然。这种指针对于无锁数据结构非常有用。