线程安全入侵_ptr
Thread-safe intrusive_ptr
boost::intrusive_ptr
(或自制版本)最简单的版本如下:
template<typename T>
class intrusive_ptr {
public:
intrusive_ptr(T* ptr) : ptr_(ptr)
{
if (ptr_) {
intrusive_ptr_add_ref(ptr_);
}
}
intrusive_ptr(const intrusive_ptr& that) : ptr_(that.ptr_)
{
if (ptr_) {
intrusive_ptr_add_ref(ptr_);
}
}
~intrusive_ptr()
{
if (ptr_) {
intrusive_ptr_release(ptr_);
}
}
// ...
private:
ptr_;
};
用法:
class Foo {
public:
// ...
private:
std::size_t refcount_;
friend void intrusive_ptr_add_ref(const Foo* p)
{
++p->refcount_;
}
friend void intrusive_ptr_release(const Foo* p)
{
if (--p->refcount_ == 0) { // line 1
delete p; // line 2
}
}
};
intrusive_ptr<Foo> p(new Foo);
显然,由于现在实现了Foo
,所以intrusive_ptr<Foo>
不是线程安全的。简单地将Foo::refcount_
的类型更改为std::atomic<std::size_t>
也不够,因为当一个线程位于第1行和第2行之间时,另一个线程可能会尝试增加引用计数。
所以我的问题是:是否有可能使intrusive_ptr
线程安全,理想情况下不使用像互斥这样的重机制?
所以我的问题是:有可能使intrusive_ptr线程安全吗,理想情况下不使用像互斥这样的重机制?
是的。将计数器更改为std::atomic就足够了,因为如果线程A将计数器值减为零,则可以保证没有其他入侵_ptr<>对象指向对象p。(因为如果确实存在,refcount值仍将大于零)。
所以你担心的比赛状况是不可能发生的。(好吧,如果其他线程取消引用指向对象p的原始指针,而不是持有intrusive_ptr,则可能会发生这种情况,但在这种情况下,由于程序有缺陷,所有赌注都会被取消)
您可以选择检测指针盗窃:
#include <cstdint>
#include <atomic>
#include <cassert>
#include <stdexcept>
struct allow_zero_access {};
template<typename T>
class intrusive_ptr {
public:
intrusive_ptr(T* ptr, allow_zero_access)
: ptr_(ptr)
{
assert(ptr);
intrusive_ptr_init_ref(ptr_, allow_zero_access());
}
intrusive_ptr(T* ptr) : ptr_(ptr)
{
if (ptr_) {
intrusive_ptr_add_ref(ptr_);
}
}
intrusive_ptr(const intrusive_ptr& that) : ptr_(that.ptr_)
{
if (ptr_) {
intrusive_ptr_add_ref(ptr_);
}
}
intrusive_ptr& operator=(const intrusive_ptr& that)
{
intrusive_ptr tmp(that);
std::swap(this->ptr_, tmp.ptr_);
return *this;
}
~intrusive_ptr()
{
if (ptr_) {
intrusive_ptr_release(ptr_);
}
}
// ...
private:
T* ptr_;
};
template<class T>
struct enable_mt_intrusive_pointer
{
private:
friend void intrusive_ptr_init_ref(const enable_mt_intrusive_pointer* p, allow_zero_access)
{
assert(p);
if (p->_refcount.fetch_add(1) != 0) {
throw std::logic_error("stealing someone's pointer!");
}
}
friend void intrusive_ptr_add_ref(const enable_mt_intrusive_pointer* p, bool first_access = false)
{
assert(p);
if (p->_refcount.fetch_add(1) == 0 && !first_access) {
throw std::logic_error("resurrecting a zombie");
}
}
friend void intrusive_ptr_release(const enable_mt_intrusive_pointer* p)
{
assert(p);
switch(p->_refcount.fetch_sub(1)) {
case 1:
delete p;
break;
case 0:
throw std::logic_error("already deleted");
break;
default:
;
}
}
mutable std::atomic<std::size_t> _refcount { 0 };
};
template<class T, class...Args>
intrusive_ptr<T> make_intrusive_ptr(Args&&...args)
{
return { new T(std::forward<Args>(args)...),
allow_zero_access() };
}
class Foo : public enable_mt_intrusive_pointer<Foo>
{
public:
// ...
};
int main()
{
auto p = make_intrusive_ptr<Foo>();
}
然而,在实践中,很少有理由在c++程序中使用intrusive_ptr。即使在与外部c库接口时,其他侵入性指针也可以封装在带有自定义deleter的std::shared_ptr中。
相关文章:
- CLANG 编译器 说:变量"PTR"可能未初始化
- 在以唯一ptr为值的C++映射中,动态内存何时会被销毁
- 将 ptr 传递给 ptr 到 A 作为参数传递给 A 的函数是不好的做法吗?
- 为共享 ptr 向量实现复制 c'tor?
- 字符和整数中 **(ptr+1) 的值差异
- C++:在不中断共享的情况下通过引用传递共享 PTR?
- 如何将派生类从基 ptr 分配给 nlohmann::json
- 引用 std::shared:ptr 以避免引用计数
- 为什么我不能在不进行任何转换的情况下将浮点数放入任何类型的 ptr 中?
- 在调用函数时,ptr** 和 ptr*& 之间是否有区别,或者首选C++?
- 另一种类型的智能ptr,比如具有弱refs的unique_ptr
- 尝试打印出 *ptr++ 的值,以了解它是如何工作的
- 如何控制共享 ptr 引用计数?
- C++中的指针否定 (!ptr == NULL)
- 从const ptr*转换为ptr*时出现问题
- 这是MSVC 2013中具有共享PTR的单例的正确实现吗?
- 对唯一 ptr 无效读取的引用向量
- C++ 类型转换基础 PTR 到派生 PTR 保存在引用类中
- 如何使用非类型参数传递模板化类的 Ref 或 Ptr
- 唯一 ptr 将所有权移动到包含对象的方法