C++: Multi-threading-und Referenz-Zählung
Derzeit ive bekam einige Referenz gezählt Klassen mit den folgenden:
class RefCounted
{
public:
void IncRef()
{
++refCnt;
}
void DecRef()
{
if(!--refCnt)delete this;
}
protected:
RefCounted():refCnt(0){}
private:
unsigned refCnt;
//not implemented
RefCounted(RefCounted&);
RefCounted& operator = (RefCounted&};
};
Ich habe auch eine smart-pointer-Klasse, die Griffe Referenz zählen , obwohl seine nicht einheitlich verwendet (z.B. in ein oder zwei bits von performance-kritischen code, wo ich minimierte die Anzahl der IncRef und DecRef Anrufe).
template<class T>class RefCountedPtr
{
public:
RefCountedPtr(T *p)
:p(p)
{
if(p)p->IncRef();
}
~RefCountedPtr()
{
if(p)p->DecRef();
}
RefCountedPtr<T>& operator = (T *newP)
{
if(newP)newP->IncRef();
if(p) p ->DecRef();
p = newP;
return *this;
}
RefCountedPtr<T>& operator = (RefCountedPtr<T> &newP)
{
if(newP.p)newP.p->IncRef();
if(p) p ->DecRef();
p = newP.p;
return *this;
}
T& operator *()
{
return *p;
}
T* operator ->()
{
return p;
}
//comparison operators etc and some const versions of the above...
private:
T *p;
};
Für die Allgemeine Verwendung von den Klassen selbst ich planen, zu verwenden reader - /writer-locking-system, aber ich möchte wirklich nicht zu haben, um eine writer-Sperre für jeden einzelnen IncRef und DecRef nennen.
Ich auch nur daran gedacht, ein Szenario, in dem der Zeiger kann ungültig erklärt werden, kurz bevor die IncRef nennen, die Sie berücksichtigen sollten:
class Texture : public RefCounted
{
public:
//...various operations...
private:
Texture(const std::string &file)
{
//...load texture from file...
TexPool.insert(this);
}
virtual ~Texture()
{
TexPool.erase(this);
}
freind CreateTextureFromFile;
};
Texture *CreateTexture(const std::string &file)
{
TexPoolIterator i = TexPool.find(file);
if(i != TexPool.end())return *i;
else return new Texture(file);
}
ThreadA ThreadB t = CreateTexture("ball.png"); t->IncRef(); ...Gebrauch t... t2 = CreateTexture("ball.png");//gibt *t ... Faden aufgehängt... t->DecRef();//löscht t ... ... t2->IncRef();//FEHLER
So, ich glaube, ich muss zum ändern der ref zu zählen-Modell vollständig, der Grund, warum ich fügte hinzu, ein ref nach der Rückkehr in das design zu unterstützen Dinge wie die folgenden:
MyObj->GetSomething()->GetSomethingElse()->DoSomething();
statt:
SomeObject a = MyObj->GetSomething();
AnotherObject *b = a->GetSomethingElse();
b->DoSomething();
b->DecRef();
a->DecRef();
Gibt es eine saubere Möglichkeit für die schnelle reference counting in c++ in einer multi-threaded Umgebung?
- Verwenden
boost::shared_ptr
: boost.org/doc/libs/1_39_0/libs/smart_ptr/shared_ptr.htm - Das als std::tr1::shared_ptr in einigen Compilern sowie
Du musst angemeldet sein, um einen Kommentar abzugeben.
Machen das "reference counting" Atom-und Sie brauchen keine Sperre. In Windows ::InterlockedIncrement und ::InterlockedDecrement verwendet werden kann. In C++ 0x, Sie haben atomic<>.
Es sei denn, Sie wissen, dass es einen spezifischen Engpass würde ich nur verwenden,
boost::shared_ptr
Es ist sehr schnell, aber es ist ein bisschen Mehraufwand in den extra control block zugeordnet werden. Auf der anderen Seite hat es viele Vorteile:
boost
die, wenn Sie nicht sollten SieBeachten Sie auch, werden Sie wahrscheinlich nicht wollen, ein reader\writer-Sperre für einen ref gezählt Objekt. Die Behauptung ist minimal und der zusätzliche Aufwand wird völlig überfordern alle Vorteile, die Sie haben würde. Der shared pointer implementiert ist, mit einem chip-Ebene Atomare int operation, diese ist deutlich besser als ein normaler mutex was deutlich schneller ist als ein reader\writer lock.
std
Container haben keine threading garantiert und bewirkt, dass Sie Probleme mit dem obigen code. Zum Beispiel, wenn, während Sie die Suche im Baum, die Sie tun, eine deletion oder insertion, kann es zu undefiniertem Verhalten.Wenn Sie nicht möchten, um boost und C++0X, aber Sie wollen immer noch lockless refcounting, können Sie dies tun, indem Sie die richtige Plattform-spezifische Atomare Inkrement/atomic-Dekrement-assembly-Routinen im code. Als Beispiel ist hier die AtomicCounter Klasse, dass ich für meine Referenz zu zählen; es funktioniert unter den meisten gängigen Betriebssystemen:
https://public.msli.com/lcs/muscle/html/AtomicCounter_8h_source.html
Ja, es ist ein hässliches Durcheinander von #ganzen IFDEFs. Aber es funktioniert nicht.
osg, OpenSceneGraph hat eine solche Struktur.
leiten Sie Ihre Klassen von osg::Referenced und Sie dont care über Destruktor auch im Multi-Thread -.
erstellen Sie einfach Klassen wie :
statt:
Wollten Sie thread-sicher oder atomar thread-safe? boot::shared_ptr ist nur thread-safe. Sie brauchen noch einen "eigenen" ein shared_ptr, um es zu kopieren sicher.
Gibt es einige experimentelle Sachen, die ich gemacht habe atomar thread-sichere Verweiszählung hier bei
http://atomic-ptr-plus.sourceforge.net/ , das kann Ihnen eine Idee geben, worum es geht.
Ihr Hauptproblem ist, dass Sie nicht erwerben eine Referenz vor CreateTexture gibt. Wenn Sie open-Codierung, wie diese, die einfachste Art das zu handhaben, es ist eine Sperre um TexPool dem wird auch genommen, bei der Freigabe von Referenzen vor dem löschen, etwa so:
und:
Sagte, dass, wie andere erwähnt haben, boost::shared_ptr (aka std::tr1::shared_ptr) implementiert diese alle in eine lockless, sicher, und hat auch Unterstützung für schwache Zeiger, die Ihnen helfen, mit Ihrer textur-cache.
boost::shared_ptr und Poco::SharedPtr beide wickeln Sie dieses idiom in eine freistehende smart-pointer.
Wenn Sie wollen intrusive reference counting, wie Sie bereits oben gezeigt, Poco ' s AutoPtr ist eine gute, funktionierende Implementierung.
EDIT: ich hätte links Hinzugefügt, aber ich war zu niedrig gezielt auf die reputation. Google für die Klasse Namen, und Sie sollten Ihren Weg finden.
Muss der cache zu verwenden
boost::weak_ptr
oder ein ähnliches Konstrukt.Ich glaube, Sie müssen kritische Abschnitte für diese Besondere design. Einem Ort, wo es benötigt wird, ist CreateTexture, da man sonst in die Gefahr, mehr als eine identische textur-Objekt in das system. Und, im Allgemeinen, wenn mehrere threads zu erstellen und zu zerstören, die die gleiche textur macht es "shared mutable state".
Werfen Sie einen Blick auf dieses pdf: http://www.research.ibm.com/people/d/dfb/papers/Bacon01Concurrent.pdf
Dieser beschreibt ein Referenz-zählen-system, das nicht brauchen, sperren. (Müssen Sie die "pause" - threads ein in eine Zeit, die rechnen können als Verriegelung.) Es sammelt auch Müll Zyklen. Der Nachteil ist, dass es wesentlich komplexer ist. Es gibt auch einige wichtige Dinge, die Links als eine übung für den Leser. Wie das, was passiert, wenn ein neuer thread erstellt oder eine alte gelöscht oder der Umgang mit der Natur aus azyklischen Objekten. (Wenn Sie entscheiden dies zu tun, bitte lassen Sie mich wissen, wie Sie sloved diese.)