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

28.1 Threads über die Win API

28.1 Threads über die Win API

Zu Beginn möchte ich zeigen, wie man mit den Win API Funktionen arbeitet. Dafür habe ich mich weitestgehend an dem Beispiel aus dem MSDN gehalten, nur dass ich es ein wenig vereinfacht habe (ich habe ein paar Sicherheitsmechanismen außer Kraft gesetzt). Schauen wir uns also die komplette "main" an.

 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
64
65
66
67
					
#include <windows.h>
#include <stdlib.h>
#include <stdio.h>

// Struktur, welche der Thread benutzt
typedef struct SMyThreadData {
	int iStart;
	int iEnd;
	int iID;
} SMyThreadData, *PMyThreadData;



// Threadfunktion /////////////////////////////////////////////////////////////
DWORD WINAPI MyThreadFunction(LPVOID lpParam) {
	PMyThreadData pData;
 
	pData = static_cast<PMyThreadData>(lpParam);

	for (int iCount = pData->iStart; iCount < pData->iEnd; iCount++) {
		printf("Thread: %i - %i\n", pData->iID, iCount);
		Sleep(10);
	} // end of while

	return 0; 
} // MyThreadFunction /////////////////////////////////////////////////////////



// Hauptfunktion der Anwendung ////////////////////////////////////////////////
int main(int argc, char** argv) {
	PMyThreadData	pData1		= (PMyThreadData)HeapAlloc(	GetProcessHeap(),
									HEAP_ZERO_MEMORY,
									sizeof(SMyThreadData)	);
	PMyThreadData	pData2		= (PMyThreadData)HeapAlloc(	GetProcessHeap(),
									HEAP_ZERO_MEMORY,
									sizeof(SMyThreadData)	);

	pData1->iStart			= 0;
	pData1->iEnd			= 10;
	pData1->iID			= 1;

	pData2->iStart			= 1000;
	pData2->iEnd			= 1010;
	pData2->iID			= 2;

	DWORD		dwThreadId1;
	DWORD		dwThreadId2;

	printf("START\n");

	HANDLE		hThreadArray1	= CreateThread(NULL, 0, MyThreadFunction, pData1, 0, &dwThreadId1);
	HANDLE		hThreadArray2	= CreateThread(NULL, 0, MyThreadFunction, pData2, 0, &dwThreadId2);

	WaitForMultipleObjects(1, &hThreadArray1, TRUE, INFINITE);
	WaitForMultipleObjects(1, &hThreadArray2, TRUE, INFINITE);

	CloseHandle(hThreadArray1);
	if (pData1 != NULL) HeapFree(GetProcessHeap(), 0, pData1);

	CloseHandle(hThreadArray2);
	if (pData2 != NULL) HeapFree(GetProcessHeap(), 0, pData2);

	printf("ENDE\n");

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

Ausgabe:

START
Thread 1 - 0
Thread 1 - 1
Thread 2 - 1000
Thread 1 - 2
Thread 2 - 1001
Thread 1 - 3
Thread 2 - 1002
Thread 1 - 4
Thread 2 - 1003
Thread 1 - 5
Thread 2 - 1004
Thread 1 - 6
Thread 2 - 1005
Thread 1 - 7
Thread 2 - 1006
Thread 1 - 8
Thread 2 - 1007
Thread 1 - 9
Thread 2 - 1008
Thread 2 - 1009
ENDE
		

Wie man gleich in Zeile 1 sieht, benötigen wir ein spezielles Include, welches uns eine Reihe von Funktionsprototypen bereit stellt, mit welchen wir die Threads realisieren können. Dann, in Zeile 6 bis 10, deklariere ich eine Datenstruktur, welche ich später als Container benutzen werde, um den Threads Informationen zuzuschieben. Bei dieser Typdefinition deklariere ich auch gleich noch einen Pointer auf jene Struktur.

In den Zeilen 15 bis 26 finden Sie dann die Funktion, welche durch den Thread ausgeführt werden soll. Gleich im Header der Funktion fallen ein paar Dinge sofort ins Auge. Da wäre zunächst der Rückgabetyp. "DWORD" stand für double Word, was wiederum einem Integer gleicht. Des weiteren steht dort ein Bezeichner "WINAPI". Dies hat was damit zu tun, dass diese Funktion eigentlich in einer DLL des Betriebssystems liegt und importiert werden muss (eigentlich bedeutet dies noch viel mehr, aber darum soll es uns jetzt nicht gehen). Anschließend folgt ein beliebiger Funktionsname. Als Parameter gibt man jetzt ein s.g. "LPVOID" Zeiger an. Dies bedeutet "large pointer of void", was wiederum einen üblichen "void" Zeiger entspricht. An jenem Zeiger wird dann die Datenstruktur gehangen. An dieser Schreibweise darf, abgesehen vom Funktionsnamen, nichts verändert werden. Am besten Sie kopieren das Gerüst eins zu eins. In Zeile 18 wandeln wir jetzt unseren Übergabeparameter in einen Zeiger auf die Datenstruktur um und könn anschließend die Schleife für die Ausgabe bauen.

Im restlichen Abschnitt folgt dann die "main" Funktion, welche den Hauptthread des Programms darstellt. Was hier passiert ist nicht ganz so leicht nachvollziehbar. Zuerst wird in den Zeilen 32 bis 37, zwei Datenstrukturen des definierten Typs im Prozessheap angelegt. Das dies so ist, werden Sie später noch sehen.

In Zeile 39 bis 45 setze ich jetzt einfach ein paar beliebige Werte, mit denen die zwei Threads dann arbeiten sollen. Anschließend, in Zeile 47 und 48, erzeuge ich mir zwei "DWORD" Variablen, in welchen später die Thread ID gespeichert wird. Jene werden ich in den Beispielen nie benutzen, aber sie könnte interessant sein, wenn man mit einem Prozessexplorer auf die Laufenden Prozesse mit ihren Threads schaut. Dort wird man dann den entsprechenden Thread unter genau dieser ID wiederfinden können.

In Zeile 52 und 53 werden jetzt die zwei Threads erzeugt und auch gleich gestartet. An dieser Stelle ist es wie bereits erwähnt möglich, den Thread auch pausiert zu starten. Dafür muss man bestimmte Flags setzen, welche Sie in der Hilfe nachlesen können.

Nun folgt etwas hochgradig gefährliches. In den Zeilen 55 und 56 rufe ich zwei mal eine Funktion auf, welche darauf wartet, dass ein bestimmter Thread zum ende kommt. Normalerweise kann man der Funktion ein Timeout übergeben, welches verhindert, dass diese Funktion so lange blockiert, bis der Thread zu Ende ist. Ich übergebe zwar auch ein Timeout, allerdings in Form einer Konstanten, welche ein extrem riesiges Timeout (entspricht also Unendlich) darstellt. Ich will dies an dieser Stelle wirklich erreichen, damit ich die Threads auch wirklich erst dann freigebe, wenn sie beendet wurden. Im Normalfall sollte man so etwas vermeiden. Würde man einen solchen Code in eine Fensterklasse einbauen, hätte dies zur Folge, dass das Fenster einfriert. Man könnte es noch nicht einmal mehr schließen. Besser ist es, lieber eine Schleife zu bauen, die immer wieder diese Funktion aufruft, bis der Thread tatsächlich zum Ende gekommen ist. So hätte man in der Zwischenzeit die Möglichkeit, andere Dinge, wie z.B. das Aktualisieren einer Statusanzeige, zu realisieren. Aber unterm Strich könnte es hier trotzdem passieren, dass das Programm in dieser Schleife auf ewig hängen bleibt (z.B. wenn der Thread abgestürzt ist oder auf etwas wartet, was nicht eintritt). In diesem Fall gäbe es die Möglichkeit den Thread mit Hilfe der Funktion "TerminateThread" abzuschießen. Dies wiederum hat aber zur Folge, dass evtl. gewisse Sachen nicht beendet werden. Sperrt die Threadfunktion z.B. etwas und wird während ihrer Arbeit terminiert, kann sie logischerweise entsprechende Sachen nicht freigeben. Somit ist es also klüger, in die Struktur eine Art Flag einzubauen, welche signalisiert, dass der Thread zum Ende kommen soll. Die Threadfunktion kann dann regelmäßig auf dieses Flag prüfen und ggf. vorzeitig seine Arbeit zum Ende bringen und somit ggf. notwendige Entsperrungen vornehmen.

Ab Zeile 58 kann ich jetzt davon ausgehen, dass die Threads definitiv zum Ende gekommen sind und nichts mehr tun und ich kann sie beenden bzw. freigeben. Dazu informiere ich das Betriebssystem darüber, dass ich das entsprechende Handle nicht mehr benötige und ähnlich der Dateiarbeit, sorgt das Betriebssystem für den Rest. Natürlich muss ich noch den erzeugten Heap wieder freigeben. Da ich ihn mit "HeapAlloc" erzeugt/angefordert habe, muss ich ihn mir "HeapFree" wieder freigeben.

An der Ausgabe sieht man, dass die Threads tatsächlich gleichzeitig gearbeitet haben. Allerdings wird die Ausgabe nicht immer so aussehen, da das Betriebssystem entscheidet, wann welcher Thread wie lange arbeitet. Es werden also immer die gleichen Zeilen auftauchen, nur ihre Reihenfolge kann variieren.

Zum Seitenanfang
Zum Inhaltsverzeichnis

© Copyright by Thomas Weiß, 2009 - 2012