Twitter und Facebook-Anbindung
X
Tweet Follow @twitterapi
!!! Anbindung an twitter und facebook öffnen !!!

Wenn Ihnen mein Online-Buch gefällt,
dann bedanken Sie sich doch mit einer kleinen Spende...

31 Mutex

31 Mutexe

Ein Mutex (mutual exclusion = gegenseitiger Ausschluss), funktioniert vom Prinzip her genauso wie ein kritischer abschnitt, nur dass er folgende Problemstellung lösen kann.

Angenommen es telefoniert wieder einer, nennen wir ihn A und ein anderer, nämlich B, will auch telefonieren und wartet jetzt so lange bis A fertig ist. Nun bekommt A während des Gespräches eine Herzattacke, fällt um und kommt nicht mehr dazu aufzulegen. Mal abgesehen davon, dass sich B jetzt lieber um A kümmern sollte, muss B, wenn er noch telefonieren will, unendlich lange warten und kommt somit nie zum Zug. Mann nennt diese Situation "verhungern". Ein Mutex bietet nun beim Betreten des kritischen Abschnittes ein Timeout, so dass es möglich ist, dass man nicht ewig warten muss um den Abschnitt zu betreten. Beispielsweise könnte ja B, wenn er ein Timeout nach 5 Minuten hat, sein Handy zücken und so sein gesprächt führen. Ein Programm kann beispielsweise erst einmal was anderes machen und später wieder versuchen in den Abschnitt einzutreten.

Nun werden Sie sich bestimmt fragen, warum ich dies als Vorteil aufzähle, da wir eben gesehen haben, dass man ein solches Verhalten auch mit einem kritischen Abschnitt durchaus lösen kann. Der Unterschied bei einem Mutex liegt nun darin, dass man dieses Verhalten nicht selbst programmieren muss, sondern dass es von Haus aus vom Betriebssystem mitgeliefert wird.

Ein Mutex hat noch eine weiteren Vorteil. Man kann ihn gleich blockiert erzeugen. Dies ist z.B. praktisch, wenn man eine Klasse baut, die eine Datenbankanbindung kapseln soll. Normalerweise stehen nach dem Erzeugen eines Objektes dieser Klasse sofort alle Methoden, und somit auch alle Zugriffe auf die Datenbank, zur Verfügung. Es könnte jetzt aber eine Weile dauern, bis die Verbindung zur Datenbank tatsächlich aufgebaut ist. Wenn man jetzt eine blockierte Mutex erzeugt und sie erst für andere freigibt, wenn die Verbindung besteht, so kann niemand etwas tun, bevor die Datenbank bereit ist (vorausgesetzt, dass in allen Methoden erst versucht wird in den kritischen Abschnitt zu gelangen, bevor etwas getan wird).

Der größte Unterschied zum kritischen Abschnitt liegt aber darin, dass ein Mutex prozessübergreifend funktioniert. So kann man z.B. den mehrfachen Start eines Programms verhindern. Alternativ kann natürlich auch eine gleichzeitige Ausgabe auf der Konsole von mehreren Programmen verhindert werden.

Zum Seitenanfang
Zum Inhaltsverzeichnis

31.2 Mutexe mit Hilfe interner Bibliotheken

31.2 Mutexe mit Hilfe interner Bibliotheken

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
					
#define _AFXDLL
#include "afxmt.h"

#include <stdio.h>
#include "ThreadWrapper.h"

struct SMyThreadData {
	int	iStart;
	int	iEnd;
	int	iID;
	CMutex*	pMutex;
};



// Funktion welche durch den Thread ausgeführt wird ///////////////////////////
DWORD ThreadFunc(CThreadWrapper<SMyThreadData>* pThread) {
	__try {
		pThread->GetData().pMutex->Lock();

		for (int iCount = pThread->GetData().iStart; iCount < pThread->GetData().iEnd && !pThread->IsTerminated(); iCount++) {
			printf("Thread: %i - %i\n", pThread->GetData().iID, iCount);
			Sleep(100);
		} // end of for
	} __finally {
		pThread->GetData().pMutex->Unlock();
	} // end of try

	return 0;
} // ThreadFunc ///////////////////////////////////////////////////////////////
 


// Hauptfunktion der Anwendung ////////////////////////////////////////////////
int main(int argc, char** argv) {
	CMutex				pMutex		= new CMutex(false, NULL, NULL);
	SMyThreadData			sData1		= {0, 10, 1, hMutex};
	SMyThreadData			sData2		= {1000, 1010, 2, hMutex};

	CThreadWrapper<SMyThreadData>*	pThread1	= new CThreadWrapper<SMyThreadData>(&ThreadFunc, sData1);
	CThreadWrapper<SMyThreadData>*	pThread2	= new CThreadWrapper<SMyThreadData>(&ThreadFunc, sData2);

	printf("START\n");

	pThread1->Resume();
	pThread2->Resume();

	// Warten, bis alle Threads fertig sind
	while (!pThread1->IsFinished() || !pThread2->IsFinished()) Sleep(1);

	delete pThread1;
	delete pThread2;

	delete pMutex;
	
	printf("ENDE\n");

	return 0;
} // main /////////////////////////////////////////////////////////////////////
					

In den Zeilen 7 bis 12 wird wieder die Struktur für den Thread definiert und im Gegensatz zu eben, ist in Zeile 11 aus dem Handle ein Zeiger auf ein Mutexobjekt geworden.

In den Zeilen 17 bis 30 finden Sie jetzt wieder die Threadfunktion, welche bis auf zwei Kleinigkeiten, wieder genauso aussieht. In Zeile 19 gebe ich über das Mutexobjekt an, dass folgender Abschnitt geschützt werden soll. In Zeile 26 verlasse ich wieder diesen Abschnitt.

In der "main" Funktion hat sich auch wieder nicht viel geändert. Wieder betrifft es nur das Erzeugen des Mutex, seine Übergabe an die Struktur und seine Freigabe.

Zum Seitenanfang
Zum Inhaltsverzeichnis

31.1 Mutexe mit Hilfe der Win API

31.1 Mutexe mit Hilfe der Win API

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
					
#include <windows.h>
#include <stdio.h>
#include "ThreadWrapper.h"

struct SMyThreadData {
	int	iStart;
	int	iEnd;
	int	iID;
	HANDLE	hMutex;
};



// Funktion, welche durch den Thread ausgeführt wird //////////////////////////
DWORD ThreadFunc(CThreadWrapper<SMyThreadData>* pThread) {
	if (pThread->GetData().hMutex != NULL) {
		__try {
			WaitForSingleObject(pThread->GetData().hMutex, INFINITE);

			for (int iCount = pThread->GetData().iStart; iCount < pThread->GetData().iEnd && !pThread->IsTerminated(); iCount++) {
				printf("Thread: %i - %i\n", pThread->GetData().iID, iCount);
				Sleep(100);
			} // end of for
		} __finally {
			ReleaseMutex(pThread->GetData().hMutex);
		} // end of try
	} // end of if

	return 0;
} // ThreadFunc ///////////////////////////////////////////////////////////////



// Hauptfunktion der Anwendung ////////////////////////////////////////////////
int main(int argc, char** argv) {
	HANDLE				hMutex		= CreateMutex(NULL, FALSE, NULL);
	SMyThreadData			sData1		= {0, 10, 1, hMutex};
	SMyThreadData			sData2		= {1000, 1010, 2, hMutex};

	CThreadWrapper<SMyThreadData>*	pThread1	= new CThreadWrapper<SMyThreadData>(&ThreadFunc, sData1);
	CThreadWrapper<SMyThreadData>*	pThread2	= new CThreadWrapper<SMyThreadData>(&ThreadFunc, sData2);

	printf("START\n");

	pThread1->Resume();
	pThread2->Resume();

	// Warten, bis alle Threads fertig sind
	while (!pThread1->IsFinished() || !pThread2->IsFinished()) Sleep(1);

	delete pThread1;
	delete pThread2;

	CloseHandle(hMutex);
	
	printf("ENDE\n");

	return 0;
} // main /////////////////////////////////////////////////////////////////////
					

In den Zeilen 5 bis 10 sehen Sie wieder die Definition der Datenstruktur, welche dem Threadobjekt mit übergeben wird. Hier ist jetzt noch ein Handle für den Mutex hinzu gekommen, da ich für beide Threads den gleichen Mutex verwenden will/muss.

Wie Sie dann in den Zeilen 14 bis 30 sehen können, benutze ich für die Threads meine Templateklasse und benutze die zweite Art der Threadfunktion, welche mein Template erlaubt. Hier ist zum einen eine Bedingung hinzugekommen, welche die Gültigkeit des Handles prüft, sowie das Betreten und Verlassen des kritischen Abschnittes mit Hilfe des Mutex. Das Betreten, in Zeile 18 sollte Ihnen bekannt vorkommen. Ähnlich sah das auch mit den Threads aus, als ich direkt die Win API benutzt hatte. Als dritten Parameter kann man ein Timeout angegeben. Ich habe hier allerdings ein nahezu unendlichen Wert angegeben, was man dem Namen der Konstante entnehmen kann. In der Zeile 25 verlasse ich dann wieder den kritischen Abschnitt mit Hilfe der Funktion "ReleaseMutex".

Ab der Zeile 35 finden Sie jetzt die "main" Funktion. Dort ist jetzt lediglich das Erzeugen des Mutex, seine Freigabe und die Übergabe des Handles an die verwendete Struktur hinzugekommen. Das Anlegen eines Mutex findet in Zeile 36 statt und sollte erklärt werden. Zum einen gibt es die Möglichkeit, dem Mutex einen Namen als dritten Parameter zu verpassen. Ich übergebe hier keinen Namen, aber genau dieser Mechanismus wird dafür benutzt, um das bereits erwähnte mehrfache Starten von Programmen zu verhindern. Belegt man einen Namen, ist der erste Parameter, die Sicherheitsflags, auch ganz interessant. Über jene kann man regeln, ob das Erzeugen des Mutex fehlschlagen soll, falls es den Mutex schon im System gibt, oder ob man den vorhandenen mit nutzen möchte. So könnte man z.B. verhindern, dass zwei Programme etwas in eine Datei schreiben (der Schreibzugriff wird zwar eh durch das Betriebssystem abgefangen, jedoch könnten Ausgaben trotzdem noch gemischt kommen, wenn auch nicht vermischt). Der zweite Parameter gibt lediglich an, ab der kritische Abschnitt schon gesperrt sein soll, was nur in sehr seltenen Fällen von Nöten ist.

Das Freigeben des Mutex läuft wieder genauso wie bei den Threads, wenn man die nativen Win API Funktionen benutzt, nämlich mit der Funktion "CloseHandle".

Zum Seitenanfang
Zum Inhaltsverzeichnis

© Copyright by Thomas Weiß, 2009 - 2012