Frage zu Mutex und Objektmethoden
-
Hallo,
ich habe eine Klasse programmiert, die einfach eine laufende Nummer an verschiedene Workerthreads gibt.
Es gibt eine Instanz dieser Klasse, und alle Workerthreads rufen die Methode getnextfreeid() auf.
Um inkonsistenzen zu verhindern möchte ich Mutexe benutzen, und mir stellt sich die Frage, ob ich die Mutex-Verwaltung in die Methode packe oder es in die Workerthreads auslagern muss. Sprich vor dem Aufrufen der Methode getnextfreeid() sich den Mutex holen und nach zurückkommen aus der Methode den Mutex wieder freigeben...
class idfactory { public: idfactory(); unsigned long current_number; HANDLE idfactory_mutex; unsigned int getnextfreeid(); };idfactory::idfactory() { idfactory_mutex= CreateMutex(NULL, false, NULL); current_number=0; }; unsigned int idfactory::getnextfreeid() { WaitForSingleObject(idfactory_mutex,NULL); current_number++; ReleaseMutex(idfactory_mutex); return current_number; };Bis hierher wäre das meine bevorzugte Art und Weise...
Ich befürchte jedoch dass es so aussehen muss:unsigned int idfactory::getnextfreeid() { return ++current_number; };in jedem Workerthread an jeder Stelle wo ich eine neue ID brauche:
WaitForSingleObject(idfac->idfactory_mutex,NULL); id=idfac->getnextfreeid() ReleaseMutex(idfac->idfactory_mutex);Meine Frage ist, wird die erste Lösung funktionieren, oder muss ich die zweite Lösung benutzen?
Gruss
R
-
Die Methode wird ganz normal innerhalb des jeweiligen Threads ausgeführt - also dürften beide Lösungen das gleiche Ergebnis bringen. Ich würde die Verwaltung eher in die Methode packen, schließlich braucht es den Anwender nicht zu interessieren, wie du deine Arbeit synchronisieren willst.
-
Nur ein paar Anmerkungen:
1. Critical Sections sind schneller und effektiver und beanspruchen weitaus weniger Ressourcen. Mutexe würde ich nur einsetzen, wenn es wirklich über Prozessgerenzen hinaus geht.2. Um einen int beim Inkrementieren zu schützen benötigst Du keinen entsprechenden Schutz, denn für so etwas gibt es InterlockedIncrement &Co.
3. Dein Code
WaitForSingleObject(idfactory_mutex,NULL); current_number++; ReleaseMutex(idfactory_mutex); return current_number;ist nicht sicher! Wenn nach ReleaseMutex direkt ein anderer Thread an die Reihe kommt, hängt es vom Code ab den der C++ Compiler erzeugt hat, was zurück kommt.
Sicher wäre nur:WaitForSingleObject(idfactory_mutex,NULL); unsigned long next_number = current_number++; ReleaseMutex(idfactory_mutex); return next_number;
-
Hallo und danke für die rasche Atwort.
Ich dachte es könnte zu einem Problem führen, weil sich die "return" - Anweisung nicht innerhalb des gesicherten Bereiches befindet.
Ich dachte im schlimmsten Fall könnte ein Thread die Variable um eins erhöhen, den Mutex freigeben, dann switcht das System auf einen anderen Thread der auch die Variable um eins erhöht, den Mutex freigibt, dann ist wieder der vorherige Thread dran ist, und gibt dann eine um 2 erhöhte ID ... Und um den Returnwert auch innerhalb des gesicherten Bereiches zu haben müsste der Mutex dann von "aussen" geholt und freigegeben werden.
Auch verstehe ich nicht 100%ig den Programmausführungszeiger der Threads auf Objektinstanzen und die Nutzung derartiger Ressourcen durch ebendiese...
Hier: Wer bekommt nun letzlich das Mutex? Der Thread der seinen Programmausführungszeiger zuerst auf Waitforsingleobject in der Methode hat bzw der den Mutex zuerst vom System bekommt?Nach und nach wird es aber alles klarer.
Danke..
R
-
Hallo,
Martin Richter schrieb:
Nur ein paar Anmerkungen:
1. Critical Sections sind schneller und effektiver und beanspruchen weitaus weniger Ressourcen. Mutexe würde ich nur einsetzen, wenn es wirklich über Prozessgerenzen hinaus geht.2. Um einen int beim Inkrementieren zu schützen benötigst Du keinen entsprechenden Schutz, denn für so etwas gibt es InterlockedIncrement &Co.
Danke, ich werde mich in diese Richtung mal einlesen.
Auch danke für den Hinweis, dass mein Code nciht threadsafe ist, was ich allerdings auch schon befürchtete...Gruss
R
-
Martin Richter schrieb:
Sicher wäre nur:
WaitForSingleObject(idfactory_mutex,NULL); unsigned long next_number = current_number++; ReleaseMutex(idfactory_mutex);wenn der compiler meint er wäre schlauer als der programmierer, ist das auch nicht multithreadingtauglich. da hilft nur ein 'volatile' vor die variable 'next_number'.
-
Ja! Vertrackt, da hast Du recht, habe ich glatt vergessen...
volatile ist hier unbedingt angesagt!
-
Also nach euren Tips (DANKE!) siehts jetzt folgendermassen aus, und auf der Testumgebung scheint es zu klappen...
unsigned long idfactory::getnextfreeid() { EnterCriticalSection( &CriticalSection); volatile unsigned long next_number = current_number++; LeaveCriticalSection( &CriticalSection); return next_number; };Mein Dank an alle die Helfen!
-
Nimm lieber InterlockedIncrement...
-
Sehe ich auch so. Der Unterschied in der Geschindigkeit ist wahrscheinlich irrsinnig!
Das haben wir bei der ganzen Diskussion um "volatile" vergessen.
Die Interlocked... Funktionen sind ja genau dafür geschaffen!
-
Anscheinend kann ich mit InterlockedIncrement aber keine unsigned longs inkrementieren...
unsigned long idfactory::getnextfreeid() { //EnterCriticalSection( &CriticalSection); volatile unsigned long next_number;// = current_number++; //LeaveCriticalSection( &CriticalSection); next_number = InterlockedIncrement(¤t_number); return next_number; };Denn damit bekomme ich folgenden Fehler:
idfactory.cpp: In member function `long unsigned int idfactory::getnextfreeid()': idfactory.cpp:34: error: invalid conversion from `long unsigned int*' to `long int*' idfactory.cpp:34: error: initializing argument 1 of `LONG InterlockedIncrement(long int*)'Compiler:MinGW
Schade eigentlich, denn laut Google besteht Interlockedincrement aus ein paar Assemblerbefehlen und ist anscheinend sehr performant.
Gruss
R
-
InterlockedIncrement nimmt keinen unsigned long pointer sondern eben nur einen long. Wenn Du unbedingt einen unsigned long brauchst, dann halte intern einen long und returniere einen unsigned long.
Frage Dich selbst ob Du jemals das höchste bit benötigst.