Home » Tutorials » Object Pascal/RTL » Threads

Threads

CreateThread (Win32-API)

Das Win32-API stellt die Funktion CreateThread zur Erzeugung eines Threads zur Verfügung.

function CreateThread(lpSecurityAttributes: Pointer;
 dwStackSize: DWORD;
 lpStartAddress: TFNThreadStartRoutine;
 lpParameter: Pointer;
 dwCreationFlags: DOWRD;
 var lpThreadId: DWORD): THandle; stdcall;

Die Parameter:

lpSecurityAttributes: Zeiger auf eine Struktur mit Sicherheitsattributen (siehe auch Win32-SDK-Hilfe).

dwStackSize: Gibt die Stack-Größe für den neuen Thread in Byte an. Wird der Wert 0 übergeben, verwednet der Thread die gleiche Stack-Größe wie der primäre Thread des Prozesses. Der Speicherbereich wird automatisch im Speicherbereich des Prozesses reserviert und wird freigegeben, wenn der Thread beendet wird. Die Stack-Größe wächst, wenn nötig. Wenn nicht genügend Speicher reserviert werden kann, schlägt der Aufruf von CreateThread fehl.

lpStartAddress: Die Startadresse des neuen Threads. Normalerweise ist das die 32-Bit-Adresse einer Funktion, die über den neuen Thread ausgeführt werden soll.

lpParameter: Gibt einen 32-Bit-Parameter für die Thread-Funktion (unter lpStartAddress angegeben) an.

dwCreationFlags: Legt zusätzliche Eigenschaften zur Erzeugung des Threads fest. Wenn die CREATE_SUSPENDED-Eigenschaft angegeben wird, wird der Thread in suspendiertem Zustand erzeugt und läuft erst ab, wenn er über ResumeThread aufgerufen wird. Wenn dieser Wert 0 ist, wird der Thread sofort nach Erzeugung gestartet.

lpThreadID: Diese Variable enthält nach der Erzeugung des Threads dessen ID, über die während seiner Laufzeit auf ihn zugegriffen werden kann.
Der Rückgabewert der Funktion ist ungleich 0, sofern der Aufruf erfolgreich war. Im Fehlerfall wird 0 zurückgegeben. Der genaue Fehler lässt sich dann über die Win32-API-Funktion GetLastError ermitteln.

Beispiel: Unsynchronisierter Thread

Als Beispiel erstellen wir nun eine kleine Konsolenanwendung (Datei/Neu/Konsolenanwendung) mit folgendem Code:

program thread;
{$APPTYPE CONSOLE}

uses Windows;

function UnserThread(zahl: Pointer): LongInt; stdcall;
begin
 Sleep(2000);
 WriteLn('UnserThread ist fertig');
 Result:=0;
end;

var
 ThreadID: DWORD;       //Thread-ID
 ThreadHandle: THandle; //Rückgabewert von CreateThread

begin
 WriteLn;
 WriteLn('Unser Thread-Testprogramm ist gestartet.');
 WriteLn('Nun erzeugen wir den neuen Thread!');

 ThreadHandle:=CreateThread(nil, 0, TFNThreadStartRoutine(@UnserThread),
 nil, 0, ThreadID);

 //wenn der Thread erfolgreich gestartet wurde (ThreadHandle<>0), können
 //wir ThreadHandle wieder freigeben:
 if ThreadHandle<>0 then CloseHandle(ThreadHandle);

 WriteLn('Das Hauptprogramm ist nun am Ende angekommen.');

 //Automatisches Schließen der Konsole verhindern:
 ReadLn;
end.

Ob überraschend oder nicht – die Ausgabe dieses Programms sieht so aus:

Warum?
Durch das Sleep wird der Thread (bestehend aus der Funktion UnserThread) so lange ausgebremst, bis das Hauptprogramm am Ende angekommen ist und das auch auf dem Bildschirm ausgegeben hat. Hätten wir auf die ReadLn-Bremse am Ende verzichtet, wäre das Konsolenfenster bereits geschlossen, noch bevor der Thread durchgelaufen ist.
Hätten wir den Thread mit dem Parameter CREATE_SUSPENDED (fünftes Argument von CreateThread) erzeugt, wäre dieser erst losgelaufen, wenn wir ihn im primären Thread (also dem Hauptprogramm) mit

ResumeThread(ThreadHandle);

aktiviert hätten. Logischerweise muss dieser Aufruf vor dem Freigeben des Handles mit CloseHandle erfolgen und sollte von der Bedingung ThreadHandle0 abhängig sein. Denn wer einen Thread, der aus irgend einem Grund nicht erzeugt werden konnte, starten will, wird mit einer Fehlermeldung bestraft.

BeginThread

In der Unit SysUtils befindet sich eine Funktion namens BeginThread. Sie kapselt den Aufruf der oben beschriebenen CreateThread-Funktion. Zusätzlich setzt sie die globale Variable IsMultiThread und macht dabei den Heap thread-sicher. Deshalb sollte auf die Verwendung von CreateThread verzichtet und stattdessen BeginThread verwendet werden.

function BeginThread(SecurityAttributes: Pointer; StackSize: LongWord;
 ThreadFunc: TThreadFunc; Parameter: Pointer; CreationFlags: LongWord;
 var ThreadId: LongWord): Integer;

Die Parameter entsprechen den bei CreateThread beschriebenen. Außerdem befinden sich Informationen dazu in der VCL-Hilfe.