我正在研究一個多線程程序,但有一個UI組件,它廣泛使用std :: shared_ptr來管理元素。我可以保證只有一個線程會使用這些shared_ptrs。創建一個非線程安全shared_ptr
有沒有辦法定義一個不會導致線程安全引用計數開銷的shared_ptr?
它可能基於boost :: shared_ptr或std :: shared_ptr。
編輯:感謝提及intrusive_ptr的答案。我忽略提及我還需要weak_ptr功能,以便排除它。
我正在研究一個多線程程序,但有一個UI組件,它廣泛使用std :: shared_ptr來管理元素。我可以保證只有一個線程會使用這些shared_ptrs。創建一個非線程安全shared_ptr
有沒有辦法定義一個不會導致線程安全引用計數開銷的shared_ptr?
它可能基於boost :: shared_ptr或std :: shared_ptr。
編輯:感謝提及intrusive_ptr的答案。我忽略提及我還需要weak_ptr功能,以便排除它。
您可以使用intrusive_ptr,因爲它允許您提供自己的引用計數。如果引用計數是變量的簡單增加/減少,那麼可能不會得到比這更好的性能。
謝謝,但我也需要weak_ptr功能 – mpipe3
我有代碼複製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不會有明顯的區別。
是的,我確實有一種情況,成千上萬的shared_ptrs可能會被複制,因此有興趣去除線程安全 – mpipe3
我建議與升壓入侵智能指針。
還有一個從斯科特·邁爾(這裏:http://www.aristeia.com/BookErrata/M29Source.html)的執行情況發表在「More Effective C++」
然而,如果有幫助,我猛地一個簡單的引用計數指針(與
多態分配
和一些支持定製deletors)。 這一個決定了無線讀取。
注意:我誤解了這一點。多態性的作用在antoher項目中有所變化。我也有,但它不支持定製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;
}
加速提供您可以定義將不會使用線程安全的引用計數的宏。
這不是一個構建時間的事情嗎?即用非線程安全參考計數來構建boost庫。我需要在同一代碼庫中的其他地方進行線程安全ref計數。 – mpipe3
是開銷真的那麼糟糕? – Anycorn
同意。開銷很小,不應該成爲瓶頸。如果你在某個地方遇到瓶頸問題,它不在shared_ptr中。 – inestical
@inestical:對於這種情況,你可能是對的,但不要忘記,shared_ptr在堆上分配它的refcount。這樣做一百萬次可能會導致嚴重的開銷。例如http://stackoverflow.com/questions/3628081/shared-ptr-horrible-speed – stijn