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...

32 Semaphore

32 Semaphore

Ein Semaphor stellen einen weiteren Mechanismus bereit, um einen gegenseitigen Ausschluss zu erreichen. Auch sie werden wieder durch das Betriebssystem bereitgestellt.

Wo liegt jetzt der Unterschied zu einem Mutex? Eine Semaphor kann jetzt mehreren Threads gleichzeitig die Nutzung einer Ressource gestatten, aber nicht allen. Dies muss man sich wie bei einem ISDN Anschluss vorstellen. Jener besitzt zwei Leitungen zum Telefonieren, also kann es zwei Leuten gestattet sein, zu telefonieren. Kommt jetzt ein dritter, so bekommt er ein Besetztzeichen und kann erst telefonieren, wenn einer der Anderen aufgelegt hat.

Alternativ kann man sich eine Semaphor auch wie eine Schale vorstellen, in welcher Kugeln liegen oder eine Schlüsselwand in einem Hotel. Immer wenn ein neuer Gast ins Hotel an die Rezeption kommt, braucht er nur an die Wand zu schauen und weiß sofort, ob er eines der Zimmer betreten kann. Hängt kein Schlüssel an der Wand, kann er aber die Empfangsdame fragen, ob demnächst ein Zimmer frei wird und ggf. warten.

Genauso wie bei den Mutexen, bieten auch Semaphore Timeouts, damit es nicht vorkommen kann, dass ein Thread dauerhaft blockiert. Bei Servern wird dies so genutzt, dass wenn zu viele Anfragen kommen, auf jene, welche nicht mehr die Semaphore überwinden, eine Nachricht zurück gesendet wird, dass der Server momentan ausgelastet ist und es der Anwender doch bitte später noch einmal probieren soll (Fehler 500).

Zum Seitenanfang
Zum Inhaltsverzeichnis

32.1 Semaphore mit Hilfe der Win API

32.1 Semaphore mit Hilfe der Win API

In diesem Beispiel werde ich drei Threads beauftragen, etwas auf der Konsole auszugeben, wobei ich es nur zwei von ihnen gestatte, in den kritischen Abschnitt einzutreten. Als Resultat sollte man dann in der Ausgabe sehen, dass sich zunächst zwei Threads abwechseln und ganz am Ende erst der Dritte alleine seine Ausgabe macht, weil er die ganze Zeit warten musste. Damit die Sache übersichtlich bleibt, machen alle Threads jetzt nur noch fünf Ausgaben.

 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
60
61
62
					
#include <windows.h>
#include <stdio.h>
#include "ThreadWrapper.h"

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



// Funktion welche durch den Thread ausgeführt wird ///////////////////////////
DWORD ThreadFunc(CThreadWrapper<SMyThreadData>* pThread) {
	__try {
		WaitForSingleObject(pThread->GetData().hSemaphor, 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 {
		ReleaseSemaphore(pThread->GetData().hSemaphor, 1, NULL);
	} // end of try

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


// Hauptfunktion der Anwendung ////////////////////////////////////////////////
int main(int argc, char** argv) {
	HANDLE				hSemaphor	= CreateSemaphore(NULL, 2, 2, NULL);

	SMyThreadData			sData1		= {0, 5, 1, hSemaphor};
	SMyThreadData			sData2		= {1000, 1005, 2, hSemaphor};
	SMyThreadData			sData3		= {10000, 10005, 3, hSemaphor};

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

	printf("START\n");

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

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

	delete pThread1;
	delete pThread2;
	delete pThread3;

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

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

Ausgabe:

START
Thread: 1 - 0
Thread: 2 - 1000
Thread: 1 - 1
Thread: 2 - 1001
Thread: 1 - 2
Thread: 2 - 1002
Thread: 1 - 3
Thread: 2 - 1003
Thread: 1 - 4
Thread: 2 - 1004
Thread: 3 - 10000
Thread: 3 - 10001
Thread: 3 - 10002
Thread: 3 - 10003
Thread: 3 - 10004
ENDE
		

Bis einschließlich Zeile 23 ist wieder alles so, wie vorhin bei den Mutexen. Ich benutze wieder ein Handle und rufe auch wieder "WaitForSingleObject" auf, um in den kritischen Abschnitt zu gelangen.

Erst ab Zeile 24 gibt es einen kleinen Unterschied. Hier wird jetzt der Abschnitt wieder verlassen, aber man kann hier der entsprechenden Funktion auf einmal mehrere Parameter übergeben. Im Grunde ist der dritte Parameter meist unwichtig und als zweiten Parameter gibt man "1" an. Aber für was steht die 1? Bei einer Semaphore können ja immer nur eine feste Anzahl von Threads in einen solchen Abschnitt eintreten. Wie viele eingetreten sind wird mitgezählt. Wenn man jetzt austritt, muss man sagen, wie viele austreten und das ist im Normalfall einer. Es kann jedoch vorkommen, dass zwei Threads in diesem Bereich sind und einer den anderen terminiert. Da der Andere jetzt keine Chance mehr hatte den Abschnitt offiziell zu verlassen, kann der erste Thread, beim verlassen des Abschnittes, dem Betriebssystem mitteilen, dass jetzt insgesamt zwei weniger da sind.

In Zeile 33 wird jetzt die Semaphore angelegt. Hier gibt es jetzt etwas zu beachten. Wie Sie sich denken können, kommt jetzt natürlich ein Parameter hinzu, mit welchen man sagen kann, wie viele die Semaphore benutzen können. Außerdem hat man wieder die Möglichkeit, den kritischen Abschnitt blockiert zu Erzeugen, wobei man diese Angabe genau anders herum zu verstehen hat, als bei den Mutexen. Initialisiert man einen Mutex mit 1, heißt dies, es ist einer im kritischen Abschnitt drin und somit kann kein anderer. Initialisiert man die Semaphor mit 1, heißt dies, dass noch einer kann, weil es noch eine Kugel in der Schale gibt oder weil noch ein Schlüssel an der Wand hängt. Ich erlaube also zwei Threads den Zugriff (erlaube maximal zwei Kugeln) und sage, dass momentan zwei Threads können (ich lege diese zwei Kugeln in die Schale).

Zum Seitenanfang
Zum Inhaltsverzeichnis

32.2 Semaphore mit Hilfe interner Bibliotheken

32.2 Semaphore 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
60
61
62
63
					
#define _AFXDLL
#include <afxmt.h>

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

struct SMyThreadData {
	int	iStart;
	int	iEnd;
	int	iID;
	CSemaphore*	pSemaphor;
};


// Funktion welche durch den Thread ausgeführt wird ///////////////////////////
DWORD ThreadFunc(CThreadWrapper<SMyThreadData>* pThread) {
	__try {
		pThread->GetData().pSemaphor->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().pSemaphor->Unlock(1, NULL);
	} // end of try

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



// Hauptfunktion der Anwendung ////////////////////////////////////////////////
int main(int argc, char** argv) {
	CSemaphore*			pSemaphor	= new CSemaphore(2, 2, NULL, NULL);

	SMyThreadData			sData1		= {0, 5, 1, pSemaphor};
	SMyThreadData			sData2		= {1000, 1005, 2, pSemaphor};
	SMyThreadData			sData3		= {10000, 10005, 3, pSemaphor};

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

	printf("START\n");

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

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

	delete pThread1;
	delete pThread2;
	delete pThread3;

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

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

Ich denke, an dieser Stelle brauche ich nicht noch einmal erklären, was sich geändert hat, da ich genauso vorgegangen bin, wie bei den Anderen auch. Das einzige worauf ich Sie hinweisen möchte ist, dass beim Erzeugen des Semaphorobjekts die Reihenfolge der Parameter anders ist.

Zum Seitenanfang
Zum Inhaltsverzeichnis

© Copyright by Thomas Weiß, 2009 - 2012