J. *_*Doe 11 c++ multithreading smart-pointers atomic thread-safety
std::shared_ptr
有像atomic_compare_exchange_weak
和家人一样的原子操作的专业,但我找不到有关等效专业的文档std::unique_ptr
.有吗?如果没有,为什么不呢?
没有标准的原子功能std::unique_ptr
.
我确实在Herb Sutter的Atomic Smart Pointers(N4058)中找到了一个论据
劳伦斯·克劳尔回应说:
shared_ptr锁定的原因之一是避免我们削弱原子模板参数的前提条件这是微不足道的情况,因此没有死锁的风险.
也就是说,我们可以削弱需求,以便参数类型只需要是lockfree,或者可能只是非递归锁定.
然而,尽管微不足道的事情具有合理的可测性特征,但我认为没有有效的机制来测试较弱的特性.
该提议已分配给并发子组,并且尚未处置.您可以在JTC1/SC22/WG21 - Papers 2014 mailing2014-07 查看状态
可以提供原子实例std::shared_ptr
并且不可能这样做的std::unique_ptr
原因在其签名中暗示.相比:
std::shared_ptr<T>
VSstd::unique_ptr<T, D>
D
Deleter的类型在哪里.std::shared_ptr
需要分配一个控制块,其中保持强弱计数,因此删除器的类型擦除是以微不足道的成本(一个简单的稍大的控制块).
因此,布局std::shared_ptr<T>
通常类似于:
template <typename T>
struct shared_ptr {
T* _M_ptr;
SomeCounterClass<T>* _M_counters;
};
Run Code Online (Sandbox Code Playgroud)
并且可以原子地执行这两个指针的交换.
std::unique_ptr
有一个零开销政策; std::unique_ptr
与使用原始指针相比,使用a 不应产生任何开销.
因此,布局std::unique_ptr<T, D>
通常类似于:
template <typename T, typename D = default_delete<T>>
struct unique_ptr {
tuple<T*, D> _M_t;
};
Run Code Online (Sandbox Code Playgroud)
其中tuple
使用EBO(空基优化)使得每当D为零大小时sizeof(unique_ptr<T>) == sizeof(T*)
.
但是,在D
不是零大小的情况下,实现归结为:
template <typename T, typename D = default_delete<T>>
struct unique_ptr {
T* _M_ptr;
D _M_del;
};
Run Code Online (Sandbox Code Playgroud)
这D
是踢球者; 一般来说,不可能保证D
可以在不依赖互斥体的情况下以原子方式进行交换.
因此,不可能std::atomic_compare_exchange*
为通用提供一套专门的例程std::unique_ptr<T, D>
.
请注意,该标准甚至不保证sizeof(unique_ptr<T>) == sizeof(T*)
AFAIK,尽管它是一种常见的优化.
要小心,unique_ptr
即使指针本身是原子的,在线程之间共享可修改的内容也很少有意义.如果其内容发生变化,其他线程如何知道呢?他们不能.
考虑这个例子:
unique_ptr<MyObject> p(new MyObject);
// Thread A
auto ptr = p.get();
if (ptr) {
ptr->do_something();
}
// Thread B
p.reset();
Run Code Online (Sandbox Code Playgroud)
线程A如何在调用后避免使用悬空指针p.get()
?
如果要在线程之间共享对象,请使用shared_ptr
具有完全针对此目的的引用计数.
如果你真的想要它,你可以随时自己动手atomic_unique_ptr
,简而言之:
#pragma once
#include <atomic>
#include <memory>
template<class T>
class atomic_unique_ptr
{
using pointer = T *;
std::atomic<pointer> ptr;
public:
constexpr atomic_unique_ptr() noexcept : ptr() {}
explicit atomic_unique_ptr(pointer p) noexcept : ptr(p) {}
atomic_unique_ptr(atomic_unique_ptr&& p) noexcept : ptr(p.release()) {}
atomic_unique_ptr& operator=(atomic_unique_ptr&& p) noexcept { reset(p.release()); return *this; }
atomic_unique_ptr(std::unique_ptr<T>&& p) noexcept : ptr(p.release()) {}
atomic_unique_ptr& operator=(std::unique_ptr<T>&& p) noexcept { reset(p.release()); return *this; }
void reset(pointer p = pointer()) { auto old = ptr.exchange(p); if (old) delete old; }
operator pointer() const { return ptr; }
pointer operator->() const { return ptr; }
pointer get() const { return ptr; }
explicit operator bool() const { return ptr != pointer(); }
pointer release() { return ptr.exchange(pointer()); }
~atomic_unique_ptr() { reset(); }
};
template<class T>
class atomic_unique_ptr<T[]> // for array types
{
using pointer = T *;
std::atomic<pointer> ptr;
public:
constexpr atomic_unique_ptr() noexcept : ptr() {}
explicit atomic_unique_ptr(pointer p) noexcept : ptr(p) {}
atomic_unique_ptr(atomic_unique_ptr&& p) noexcept : ptr(p.release()) {}
atomic_unique_ptr& operator=(atomic_unique_ptr&& p) noexcept { reset(p.release()); return *this; }
atomic_unique_ptr(std::unique_ptr<T>&& p) noexcept : ptr(p.release()) {}
atomic_unique_ptr& operator=(std::unique_ptr<T>&& p) noexcept { reset(p.release()); return *this; }
void reset(pointer p = pointer()) { auto old = ptr.exchange(p); if (old) delete[] old; }
operator pointer() const { return ptr; }
pointer operator->() const { return ptr; }
pointer get() const { return ptr; }
explicit operator bool() const { return ptr != pointer(); }
pointer release() { return ptr.exchange(pointer()); }
~atomic_unique_ptr() { reset(); }
};
Run Code Online (Sandbox Code Playgroud)
注意:此帖中提供的代码现已发布到公共领域.