我正在研究一个多线程程序,但是有一个UI组件,它广泛使用std :: shared_ptr来管理元素。我可以保证只有一个线程会使用这些shared_ptrs。
有没有办法定义一个不会导致线程安全引用计数开销的shared_ptr?
它可以基于boost :: shared_ptr或std :: shared_ptr。
编辑:感谢您提及intrusive_ptr的答案。我忽略了提到我还需要weak_ptr功能,以便将其排除在外。
更新:我的答案是使用Boost的local_shared_ptr。请参阅'他漫步'的评论
答案 0 :(得分:4)
Andrei Alexandrescu在CppCon 2014上讨论了实现自己的单线程共享指针类(带有一些额外的优化)
观看视频here
幻灯片here
我真的认为标准或者提升应该提供一个模板参数,用于在共享的ptrs中使用原子引用计数...
答案 1 :(得分:3)
您可以使用intrusive_ptr,因为它允许您提供自己的引用计数。如果该引用计数是变量的简单增量/减量,则可能不会获得比该变量更好的性能。
答案 2 :(得分:1)
我有代码,其中复制shared_ptr
的开销已经成为问题,并且在那时使用了替代技术。让我首先证明其他评论是正确的,shared_ptr
的开销非常低。我对此进行了分析,实际上找到了我的一个麻烦点。在我的AMD64 Phenom上调用复制shared_ptr
的函数大约需要12ns而不是用普通指针调用相同的函数大约1ns。
考虑到这些数字,很难想象你会在原始指针和shared_ptr
之间得到任何类型的“安全”变体。所以我在这种情况下做的是将实际指针或const &
传递给shared_ptr
。通常我会在代码的整个部分放置一个互斥锁,这样我就可以保证在整个持续时间内保持shared_ptr。您可以手动滚动单线程引用计数,但如果您知道它没有被共享,那将是什么意思?
但要仔细考虑时间安排。除非您每秒复制shared_ptr
数千甚至数万次,否则您将不会注意到shared_ptr的开销。
在同一项目的GUI代码中,我总是使用shared_ptr,只有服务器代码在几个关键区域中避免使用它。 GUI中还有很多其他东西可以减慢速度:避免使用shared_ptr会产生明显的差异。
答案 3 :(得分:1)
我建议使用Boost侵入式智能指针。
Scott Meyer(此处:http://www.aristeia.com/BookErrata/M29Source.html)的实施也发布在“More Effective C++”
中但是,如果它有帮助,我猛拉了一个简单的引用计数指针(支持多态分配和自定义deletors)。 这个是决定不感知的。
注意:我错误地记错了。对于其他项目,多态异常是一种变异。我也有,但它不支持自定义deletor :)让我知道是否有人感兴趣;当然,它附带了功能
的单独单元测试
它附带单元测试(例如,检查着名的remove linked list node
排序错误)。所以你知道你得到了什么:)
/*
* counted_ptr - simple reference counted pointer.
*
* The is a non-intrusive implementation that allocates an additional
* int and pointer for every counted object.
*/
#ifndef COUNTED_PTR_H
#define COUNTED_PTR_H
#include <stdlib.h>
extern "C" bool mtx_unit_test_countedptr();
namespace MtxChess {
/* For ANSI-challenged compilers, you may want to #define
* NO_MEMBER_TEMPLATES or explicit */
template <class X>
struct FreeMallocPolicy
{
static void do_free(X* p) { if (p) ::free(p); p = 0; }
};
template <class X>
struct ScalarDeletePolicy
{
static void do_free(X* p) { if (p) delete p; p = 0; }
};
template <class X>
struct ArrayDeletePolicy
{
static void do_free(X* p) { if (p) delete[] p; p = 0; }
};
template <class X,class _P=ScalarDeletePolicy<X> > class counted_ptr
{
public:
typedef X element_type;
explicit counted_ptr(X* p = 0) // allocate a new counter
: itsCounter(0) {if (p) itsCounter = new counter(p);}
~counted_ptr()
{release();}
counted_ptr(const counted_ptr& r) throw()
{acquire(r.itsCounter);}
operator bool() const { return 0!=get(); }
void clear() { (*this) = counted_ptr<X>(0); }
counted_ptr& operator=(const counted_ptr& r)
{
if (this != &r) {
auto_release keep(itsCounter);
acquire(r.itsCounter);
}
return *this;
}
bool operator<(const counted_ptr& r) const
{
return get()<r.get();
}
bool operator==(const counted_ptr& r) const
{
return get()==r.get();
}
bool operator!=(const counted_ptr& r) const
{
return get()!=r.get();
}
#ifndef NO_MEMBER_TEMPLATES
// template <class Y> friend class counted_ptr<Y>;
template <class Y> counted_ptr(const counted_ptr<Y>& r) throw()
{acquire(r.itsCounter);}
template <class Y> counted_ptr& operator=(const counted_ptr<Y>& r)
{
if (this != &r) {
auto_release keep(itsCounter);
acquire(r.itsCounter);
}
return *this;
}
template <class Y> bool operator<(const counted_ptr<Y>& r) const
{
return get()<r.get();
}
template <class Y> bool operator==(const counted_ptr<Y>& r) const
{
return get()==r.get();
}
template <class Y> bool operator!=(const counted_ptr<Y>& r) const
{
return get()!=r.get();
}
#endif // NO_MEMBER_TEMPLATES
X& operator*() const throw() {return *itsCounter->ptr;}
X* operator->() const throw() {return itsCounter->ptr;}
X* get() const throw() {return itsCounter ? itsCounter->ptr : 0;}
bool unique() const throw()
{return (itsCounter ? itsCounter->count == 1 : true);}
private:
struct counter {
counter(X* p = 0, unsigned c = 1) : ptr(p), count(c) {}
X* ptr;
unsigned count;
}* itsCounter;
void acquire(counter* c) throw()
{
// increment the count
itsCounter = c;
if (c) ++c->count;
}
void release()
{
dorelease(itsCounter);
}
struct auto_release
{
auto_release(counter* c) : _c(c) {}
~auto_release() { dorelease(_c); }
counter* _c;
};
void static dorelease(counter* itsCounter)
{
// decrement the count, delete if it is 0
if (itsCounter) {
if (--itsCounter->count == 0) {
_P::do_free(itsCounter->ptr);
delete itsCounter;
}
itsCounter = 0;
}
}
};
} // EON
#endif // COUNTED_PTR_H
单元测试(单独编译)
/*
* counted_ptr (cpp) - simple reference counted pointer.
*
* The is a non-intrusive implementation that allocates an additional
* int and pointer for every counted object.
*/
#include "counted_ptr.hpp"
#include "internal.hpp"
#include <map>
#include <string>
namespace MtxChess {
namespace /*anon*/
{
// sensed events
typedef std::map<std::string, int> Events;
static Events constructions, destructions;
struct Trackable
{
Trackable(const std::string& id) : _id(id) { constructions[_id]++; }
~Trackable() { destructions[_id]++; }
const std::string _id;
};
typedef counted_ptr<Trackable> target_t;
bool testBehaviour()
{
static const counted_ptr<Trackable> Nil = target_t(0);
bool ok = true;
constructions.clear();
destructions.clear();
MTXASSERT_EQ(ok, 0ul, constructions.size());
MTXASSERT_EQ(ok, 0ul, destructions.size());
target_t a = target_t(new Trackable("aap"));
MTXASSERT_EQ(ok, 1ul, constructions.size());
MTXASSERT_EQ(ok, 1, constructions["aap"]);
MTXASSERT_EQ(ok, 0ul, destructions.size());
MTXASSERT_EQ(ok, 0, constructions["noot"]);
MTXASSERT_EQ(ok, 2ul, constructions.size());
MTXASSERT_EQ(ok, 0ul, destructions.size());
target_t hold;
{
target_t b = target_t(new Trackable("noot")),
c = target_t(new Trackable("mies")),
nil = Nil,
a2 = a;
MTXASSERT(ok, a2==a);
MTXASSERT(ok, nil!=a);
MTXASSERT_EQ(ok, 3ul, constructions.size());
MTXASSERT_EQ(ok, 1, constructions["aap"]);
MTXASSERT_EQ(ok, 1, constructions["noot"]);
MTXASSERT_EQ(ok, 1, constructions["mies"]);
MTXASSERT_EQ(ok, 0, constructions["broer"]);
MTXASSERT_EQ(ok, 4ul, constructions.size());
MTXASSERT_EQ(ok, 0ul, destructions.size());
hold = b;
}
MTXASSERT_EQ(ok, 1ul, destructions.size());
MTXASSERT_EQ(ok, 0, destructions["aap"]);
MTXASSERT_EQ(ok, 0, destructions["noot"]);
MTXASSERT_EQ(ok, 1, destructions["mies"]);
MTXASSERT_EQ(ok, 3ul, destructions.size());
hold = Nil;
MTXASSERT_EQ(ok, 3ul, destructions.size());
MTXASSERT_EQ(ok, 0, destructions["aap"]);
MTXASSERT_EQ(ok, 1, destructions["noot"]);
MTXASSERT_EQ(ok, 1, destructions["mies"]);
MTXASSERT_EQ(ok, 4ul, constructions.size());
// ok, enuf for now
return ok;
}
struct Linked : Trackable
{
Linked(const std::string&t):Trackable(t){}
counted_ptr<Linked> next;
};
bool testLinked()
{
bool ok = true;
constructions.clear();
destructions.clear();
MTXASSERT_EQ(ok, 0ul, constructions.size());
MTXASSERT_EQ(ok, 0ul, destructions.size());
counted_ptr<Linked> node(new Linked("parent"));
MTXASSERT(ok, node.get());
node->next = counted_ptr<Linked>(new Linked("child"));
MTXASSERT_EQ(ok, 2ul, constructions.size());
MTXASSERT_EQ(ok, 0ul, destructions.size());
node = node->next;
MTXASSERT(ok, node.get());
MTXASSERT_EQ(ok, 2ul, constructions.size());
MTXASSERT_EQ(ok, 1ul, destructions.size());
node = node->next;
MTXASSERT(ok,!node.get());
MTXASSERT_EQ(ok, 2ul, constructions.size());
MTXASSERT_EQ(ok, 2ul, destructions.size());
return ok;
}
}
} // EON
int main()
{
using namespace MtxChess;
bool ok = true;
ok = testBehaviour() && ok;
ok = testLinked() && ok;
return ok?0:1;
}
答案 4 :(得分:0)
Boost提供了一个可以定义的宏,它不会使用线程安全引用计数。
答案 5 :(得分:0)
现在将其添加为接受的答案。 Boost local_shared_ptr是一个单线程引用计数的智能指针,该指针使用非原子操作来提高速度:
https://www.boost.org/doc/libs/1_65_0/libs/smart_ptr/doc/html/smart_ptr.html#local_shared_ptr