Frage zu Threadsicherheit
-
Hi
Ich habe momentan folgendes Problem: Ich möchte eine Klasse Element und eine Klasse Container haben. Letztere soll drei Methoden - create, get und destroy - haben mit denen eine Instanz vom Typ Element erzeugt, holt (anhand irgendeiner ID) bzw. zerstört wird. Ich möchte, dass create, get und destroy threadsicher sind und außerdem, dass ich auf jeder einzelnen der Instanzen von Element in verschiedenen Threads rumnuddeln kann.
Meine Überlegung ist jetzt, dass ich dazu eine Mutex für Container und jeweils eine für jede Element Instanz brauche, d.h.:
class Element:
* Mutex m_mutexclass Container:
* create
* get
* destroy
* Mutex m_mutexSo, dann wage ich mich mal an eine Pseudoimplementierung:
Element* Container::create() { m_mutex.lock(); // Mutex des containers holen Element* tmp = new Element(); // Element erzeugen tmp->m_mutex.lock(); // Mutex des Objekt holen, wenn man es zuerst dem // Container hinzufügt, könnte jemand einfach so // dazwischenfummeln mit get und destroy und das // Ding zerstören // Dem Container irgendwie hinzufügen m_mutex.unlock(); // Container wieder freigeben return tmp; // Der Aufrufer kann es danach verwenden } Element* Container::get(ID id) { m_mutex.lock(); Element* tmp = // Die Instanz mit der ID id finden tmp->m_mutex.lock(); m_mutex.unlock(); return tmp; // Element wieder gelockt zurückgeben } void Container::destroy(Element* obj) // obj ist gelockt { m_mutex.lock(); // Das Ding aus dem Container rausschmeißen delete obj; // Objekt zerstären m_mutex.unlock(); }
Ok, nehmen wir nun an, dass wir ein Element (nennen wir es E haben) erstellt haben, und ein Thread verwendet dieses E, d.h. die Mutex von E ist gelockt. Jetzt kommt ein zweiter Thread mit einem get() Aufruf und will dieses E haben, blockiert aber in Zeile 19 (da die Mutex ja gelockt ist), dabei hat dieser Thread dann aber die Mutex des Containers. Anschließend möchte der ursprüngliche Thread E zerstören und ruft destroy auf, bleibt aber in Zeile 27 stecken, da der andere Thread ja die Mutex dieses Containers hat.
=> DeadlockD.h. meine "Lösung" ist kompletter Müll. Da das m.E. ein primitives und wahrscheinlich häufiges Szenario ist, kann ich mir nicht vorstellen, dass das prinzipiell nicht geht, sondern eher, dass ich entweder die falschen Mittel (Mutex), die falsche Anzahl oder/und den falschen Scope (also Bereich auf dem sie gehalten wird) habe.
Was ich nun suche ist eher nicht die Antwort a la "es gibt da boost::even_smarter_pointer und boost::smart_container die das alles mittels dunkler Magie in 2 Zeilen machen", sondern eher eine Lösung mit primitiveren Operationen (also zB mit Mutex, oä), deswegen steht das ganze im Unterforum "Rund um die Programmierung" und nicht in C++. Den "syntactic sugar" kann ich mir dann denke ich auch selbst zusammenfrickeln.
Danke für eure Antworten!
MfG
bluecode
-
Du könntest z.B. Bedingsvariablen verwenden, mit pthreads also pthread_cond_wait() usw.
Statt den einzelnen Element-Mutexen würde jedes Element ein einfaches Flag enthalten, das anzeigt, ob gerade jemand das Element benutzt (die Flags werden allesamt durch den einen Container-Mutex geschützt), und dazu jeweils eine Bedingungsvariable, auf die die Threads warten können, wenn sie (bei gelocktem Container-Mutex) in Container::get merken, dass das gewünschte Element schon belegt ist. Während des Wartens auf die Bedingungsvariable wird der Mutex implizit freigegeben, sodass kein Deadlock auftreten kann.
-
dein Element braucht kein mutex, der bereich ist doch schon gesichtert durch den mutex im container.
dein Element braucht nur eine variable in der du (innerhalb des gelockten mutex vom container) sagst dass du sie jetzt benutzt
in zeile 19, sollte kein zweiter lock stattfinden, sondern die abfrage, ob das element schon benutzt wird, falls nein, setzt du es auf benutzen. falls ja, musst du dir halt ausdenken wie dein programm darauf reagiert, moeglichkeiten gibt es einigea) du machst einen loop um lock/unlock
b) du gehst in eine condition und wartest auf ein notify dass dein Element frei wurde
c) du returns 0 zurueck und die ebene darueber muss ich darum kuemmern
usw.