Sign in Join
Wiki copytemporary

Multithreaded Application Tutorial/de: what changed

Revision 2 to 3. Removed lines have a minus sign; added lines have a plus sign.

OldNewLine
11Zurück zu den [[Additional information/de|Zusätzlichen Informationen]].\
22
33== Überblick ==
44
55Diese Seite soll zeigen, wie man unter FreePascal und Lazarus Multithread-Anwendungen erstellt und verwaltet. In einer Multithread-Anwendung lassen sich verschiedene Aufgaben auf mehrere Threads verteilen, die gleichzeitig ausgeführt werden können.
66
77Wenn Sie bisher keinerlei Erfahrungen zur Multithread-Programmierung gemacht haben, empfehlen wie Ihnen, sich zunächst den Artikel "Benötigt Ihre Anwendung wirklich Multithread-Eigenschaften?" sorgfältig durch zu lesen, da Multithread-Programmierung kein leichtes Unterfangen ist.
88
99Das Hauptziel der Multithread-Programmierung ist die Verfügbarkeit der Benutzeroberfläche eines Programms, während es im Hintergrund Berechnungen durchführt. Dies kann man erreichen, indem man die Berechnung in einen Thread außerhalb des sogenannten Main-Thread verlagert, welcher für die Aktualisierung der Benutzeroberfläche zuständig ist.
1010
1111Andere Anwendungen, bei denen Multithread-Programmierung zum Einsatz kommt, sind Server-Anwendungen, die mehrere Klienten gleichzeitig betreuen müssen.
1212
1313Multithread-Anwendungen ermöglichen auch die Aufteilungen der Lasten einer Berechnung auf mehrere Kerne einer Multi-Core-CPU.
1414
1515Wichtig: Der Main Thread wird beim Start Ihrer Anwendung vom Betriebssystem erstellt. Der Main Thread ist dabei der einzige Thread (und **muss** auch der einzige bleiben), der für die Aktualisierung der Komponenten der Benutzeroberfläche zuständig ist (Forms, etc.) (ansonsten hängt sich Ihre Anwendung auf).
1616
1717## Benötigt Ihre Anwendung wirklich Multithread-Eigenschaften?
1818
1919Wenn Multithread Programmierung für Sie Neuland ist und Sie lediglich eine bessere Reaktionsfähigkeit ihrer Anwendung während langer Berechnungen benötigen, dann ist Multithreading nicht unbedingt die einfachste Lösung. Multithreading Applikationen sind immer schwieriger zu debuggen und auch oft viel komplexer. Außerdem benötigen Sie in vielen Fällen kein Multithreading, um ihre Anwendung reaktionsfähig zu halten. Statt dessen können Sie ein **Application.ProcessMessages** in Ihre Berechnungen mit einbauen. Dieses verarbeitet alle anstehenden Nachrichten, die an Ihre Applikation gesendet wurden und Ihre Applikation reagiert auf Ereignisse. Sie können also einen Teil der Berechnung durchführen, dann Application.ProcessMessages aufrufen und die Benutzereingaben werden verarbeitet und die Oberfläche neu gezeichnet. Platzieren Sie das Application.Processmessages z.B. in Schleifen, so dass bei jedem Schleifendurchlauf die Benutzeroberfläche reagiert.
2020
2121Zum Beispiel: In den Lazarus-Beispielen unter examples/multithreading/singlethreadingexample1.lpi wird eine große Datei gelesen und verarbeitet und die oben genannte Technik benutzt.
2222
2323Multithreading wird wirklich benötigt für:
2424
2525- blocking handles, wie Netzwerkkommunikation, Interrupts (Linux)
2626- Mehrprozessor- oder Mehrkernbetrieb
2727- Algorithmen und Bibliotheksaufrufe, die nicht in mehrere kleine Stufen zerteilt werden können.
2828
2929## Die Klasse TThread
3030
3131Das folgende Beispiel ist im Verzeichnis examples/multithreading/ zu finden. Der einfachste Weg, um eine Multithread-Anwendung zu erstellen, ist die Verwendung der Klasse TThread. Über diese Klasse lässt sich, neben dem Main-Thread, ein zusätzlicher Thread in einfacher Weise erstellen. Unter normalen Umständen müssen dazu bloß zwei Methoden überschrieben (override) werden: Der Konstruktor Create und die Methode Execute. Im Konstruktor Create wird der Thread für die spätere Ausführung vorbereitet. Hier werden die dem Thread zugrunde liegenden Variablen initialisiert. Der originale Konstruktor des Threads enthält einen Parameter namens "Suspended": Damit der Thread nicht automatisch nach seiner Erstellung gestartet wird, ist es empfehlenswert, diesen Parameter auf "true" zu setzen. In diesem Fall können Sie den Thread zu einem späteren Zeitpunkt über die Methode "Resume" starten. Ist es dagegen erwünscht, dass der Thread direkt nach seiner Erstellung startet, setzen Sie Suspended auf "false".
3232
3333Ab der Version 2.0.1 des FreePascal-Compilers lässt sich auch die Größe des von einem Thread verwendeten Stacks über einen Parameter des Konstruktor TThread.Create einstellen. Das kann beispielsweise bei rekursiven Prozeduren oder Funktionen nützlich sein. Wird die Größe des Stack nicht explizit festgelegt, verwendet FPC die vom Betriebssystem festgelegte Standardgröße.
3434
3535In die (mit Parameter Override) überschriebene Methode Execute schreibt man den vom Thread auszuführenden Quelltext hinein.
3636
3737Die Klasse TThread besitzt die wichtige Eigenschaft (property): Terminated : boolean; (Standardeinstellung: Terminated=false)
3838
3939Der Sinn der Eigenschaft Terminated ist, dass sich ein Thread damit zu einem beliebigen Zeitpunkt abbrechen lässt. Jedoch muss dies von dem Programmierer in den Quelltext der Methode Execute eingearbeitet werden - die Methode Terminate greift also nicht direkt in den vom Programmierer vorgesehenen Programmablauf ein. Sollten sich in Ihrem Thread also Repeatschleifen oder andere sich wiederholende Stellen finden, ist es deshalb notwendig, dass Sie in jeder Schleife explizit prüfen, ob die Eigenschaft Terminated wahr oder falsch ist. Sollte Terminated=true sein, muss die Methode Execute (evtl. nach einer finalen Konfiguration) auf schnellstem Wege verlassen werden.
4040
4141Wie eingangs erwähnt, sollte der erstellte Thread in keinem Fall mit den sichtbaren Komponenten der Benutzeroberfläche agieren. Die Benutzeroberfläche verändern, Eingaben abfragen, etc. darf ausschließlich der Main-Thread.
4242
4343Um trotzdem aus einem Thread heraus auf die Benutzeroberfläche bzw. Variablen des Hauptprogrammes zuzugreifen, existiert die Methode Synchronize. Synchronize wird mit einer procedure als Parameter aufgerufen. Wenn Sie Synchronize(@MyMethod) aufrufen, wird der Thread gestoppt, der Code in MyMethod wird im Hauptthread ausgeführt, und dann die Bearbeitung des Threads fortgesetzt. Die exakte Arbeitsweise von Synchronize hängt von der Plattform ab.
4444
4545Eine andere wichtige Eigenschaft (property) von TThread ist FreeOnTerminate. Wenn diese Eigenschaft auf True gesetzt ist, wird der Thread automatisch freigegeben (wie ein Aufruf von .Free), wenn Execute beendet wird. Beispiel:
4646
4747``` pascal
4848 Type
4949 TMyThread = class(TThread)
5050 private
5151 fStatusText : string;
5252 procedure ShowStatus;
5353 protected
5454 procedure Execute; override;
5555 public
5656 Constructor Create(CreateSuspended : boolean);
5757 end;
5858
5959 constructor TMyThread.Create(CreateSuspended : boolean);
6060 begin
6161 FreeOnTerminate := True;
6262 inherited Create(CreateSuspended);
6363 end;
6464
6565 procedure TMyThread.ShowStatus;
6666 // Diese Methode wird vom MainThread ausgeführt und kann deshalb auf alle GUI-Elemente zugreifen
6767 begin
6868 Form1.Caption := fStatusText;
6969 end;
7070
7171 procedure TMyThread.Execute;
7272 var
7373 newStatus : string;
7474 begin
7575 fStatusText := 'TMyThread Starting...';
7676 Synchronize(@Showstatus);
7777 fStatusText := 'TMyThread Running...';
7878 while (not Terminated) and ([any condition required]) do
7979 begin
8080 ...
8181 // here goes the code of the main thread loop
8282 ...
8383 if NewStatus <> fStatusText then
8484 begin
8585 fStatusText := newStatus;
8686 Synchronize(@Showstatus);
8787 end;
8888 end;
8989 end;
9090```
9191
9292In der Hauptunit erfolgt dann folgender Aufruf
9393
9494``` pascal
9595 var
9696 MyThread : TMyThread;
9797 begin
9898 MyThread := TMyThread.Create(True); // So startet es nicht automatisch
9999 ...
100100 // Here the code initialises anything required before the threads starts executing
101101 ...
102102 MyThread.Resume;
103103 end;
104104```
105105
106106Soll Ihr Programm flexibler sein, können Sie ein Ereignis für den Thread erzeugen - Ihre synchronisierte Methode ist dann nicht eng an eine bestimmte Form oder Klasse gebunden und Listeners können mit dem Ereignis des Threads verbunden werden. Hier ein Beispiel:
107107
108108``` pascal
109109 Type
110110 TShowStatusEvent = procedure(Status: String) of Object;
111111
112112 TMyThread = class(TThread)
113113 private
114114 fStatusText : string;
115115 FOnShowStatus: TShowStatusEvent;
116116 procedure ShowStatus;
117117 protected
118118 procedure Execute; override;
119119 public
120120 Constructor Create(CreateSuspended : boolean);
121121 property OnShowStatus: TShowStatusEvent read FOnShowStatus write FOnShowStatus;
122122 end;
123123
124124 constructor TMyThread.Create(CreateSuspended : boolean);
125125 begin
126126 FreeOnTerminate := True;
127127 inherited Create(CreateSuspended);
128128 end;
129129
130130 procedure TMyThread.ShowStatus;
131131 // Diese Methode wird vom MainThread ausgeführt und kann deshalb auf alle GUI-Elemente zugreifen
132132 begin
133133 if Assigned(FOnShowStatus) then
134134 begin
135135 FOnShowStatus(fStatusText);
136136 end;
137137 end;
138138
139139 procedure TMyThread.Execute;
140140 var
141141 newStatus : string;
142142 begin
143143 fStatusText := 'TMyThread Starting...';
144144 Synchronize(@Showstatus);
145145 fStatusText := 'TMyThread Running...';
146146 while (not Terminated) and ([any condition required]) do
147147 begin
148148 ...
149149 [here goes the code of the main thread loop]
150150 ...
151151 if NewStatus <> fStatusText then
152152 begin
153153 fStatusText := newStatus;
154154 Synchronize(@Showstatus);
155155 end;
156156 end;
157157 end;
158158```
159159
160160Und im Hauptformular
161161
162162``` pascal
163163 Type
164164 TForm1 = class(TForm)
165165 Button1: TButton;
166166 Label1: TLabel;
167167 procedure FormCreate(Sender: TObject);
168168 procedure FormDestroy(Sender: TObject);
169169 private
170170 { private declarations }
171171 MyThread: TMyThread;
172172 procedure ShowStatus(Status: string);
173173 public
174174 { public declarations }
175175 end;
176176
177177 procedure TForm1.FormCreate(Sender: TObject);
178178 begin
179179 inherited;
180180 MyThread := TMyThread.Create(true);
181181 MyThread.OnShowStatus := @ShowStatus;
182182 end;
183183
184184 procedure TForm1.FormDestroy(Sender: TObject);
185185 begin
186186 MyThread.Terminate;
187187 MyThread.Free;
188188 inherited;
189189 end;
190190
191191 procedure TForm1.Button1Click(Sender: TObject);
192192 begin
193193 MyThread.Resume;
194194 end;
195195
196196 procedure TForm1.ShowStatus(Status: string);
197197 begin
198198 Label1.Caption := Status;
199199 end;
200200```
201201
202202## Worauf Sie achten sollten
203203
204204Wenn Sie den Compilerswitch -Ct (Stack Check) benutzen, kann das einige Probleme mit sich bringen (Originalübersetzung: Es könnte Ihnen Kopfschmerzen bereiten :)) Aus unbekannten Gründen "triggert" der Stack Check an jedem TThread.Create, wenn Sie die Standardgröße des Stacks benutzen. Die einzige Lösung ist zur Zeit, -Ct nicht zu benutzen.
205205
206206Um auf Exceptions im Thread zu prüfen, können Sie folgendermaßen vorgehen:
207207
208208``` pascal
209209 MyThread := TThread.Create(False);
210210 if Assigned(MyThread.FatalException) then
211211 raise MyThread.FatalException;
212212```
213213
214214Dieser Code sorgt dafür daß Exceptions, die während der Erzeugung des Threads auftreten, im Main Thread einen Ausnahmefehler auslösen.
215215
216216## Für Multithread-Anwendungen benötigte Units
217217
218218Unter Windows benötigen Sie keine spezielle Unit, damit es funktioniert. Unter Linux, MacOSX und FreeBSD benötigen Sie die Unit cthreads und diese *muss* die erste verwendete Unit im Projekt sein (die Programm-Unit, .lpr)!
219219
220220Daher sollte der Code Ihrer Lazarus-Anwendung etwa so aussehen:
221221
222222``` pascal
223223 program MyMultiThreadedProgram;
224224 {$mode objfpc}{$H+}
225225 uses
226226 {$ifdef unix}
227227 cthreads,
228228 {$endif}
229229 Interfaces, // dies bindet das LCL Widgetset ein
230230 Forms
231231 { fügen Sie Ihre Units hier hinzu },
232232```
233233
234234Wenn Sie dies vergessen, kommt die folgende Fehlermeldung:
235235
236-` This binary has no thread support compiled in.`\
237-` Recompile the application with a thread-driver in the program uses clause before other units using thread.`
236+``` pascal
237+ This binary has no thread support compiled in.
238+ Recompile the application with a thread-driver in the program uses clause before other units using thread.
239+```
238240
239241Lazarus Packages, die Multithreading beutzen, sollten **-dUseCThreads** zu den benutzerdefinierten Einstellungen für abhängige Pakete hinzufügen. Dazu öffnen Sie den Package-Editor für das entsprechende Paket, unter Einstellungen \> Bedienung \> Benutzerdefiniert fügen Sie *-dUseCThreads* hinzu. Dies wird diesen Compiler-Switch für alle Projekte und andere Packages, die dieses Package benutzen (die IDE eingeschlossen), definieren. Sowohl die IDE als auch alle von ihr neu erstellten Projekte, haben bereits folgenden Code in ihrer Projektdatei (.lpr):
240242
241243``` pascal
242244 uses
243245 {$IFDEF UNIX}{$IFDEF UseCThreads}
244246 cthreads,
245247 {$ENDIF}{$ENDIF}
246248```
247249
248250## SMP (Mehrprozessor) Unterstützung
249251
250252Sobald Sie Threads benutzen, werden diese vom Betriebsystem auf mehrere Prozessoren verteilt.
251253
252254## Debuging Multithread-Anwendungen mit Lazarus
253255
254256Das Debuggen von Multithread-Anwendungen wird von Lazarus zur Zeit noch nicht vollständig unterstützt.
255257
256258### Debugger Ausgabe
257259
258260In einer Anwendung mit einem Thread (dem Hauptthread), können Sie einfach auf die Konsole in ein Terminal oder ähnliches schreiben und die Reihenfolge der Zeilen wird chronologisch so angeordnet sein, wie Sie es ausgegeben haben. In Multithreaded Anwendungen wird dies etwas komplizierter. Wenn 2 Threads schreiben, sagen wir, eine Zeile wird von Thread A und eine von Thread B geschrieben, dann ist nicht gesagt, dass sie zeitlich in der richtigen Reihenfolge erscheinen, bzw. es kann vorkommen, dass ein Thread dem anderen in die Ausgabe hineinschreibt.
259261
260262Die Unit LCLProc enthält einige Funktionen, um jeden Thread seine eigene Logdatei schreiben zu lassen:
261263
262264``` pascal
263265 procedure DbgOutThreadLog(const Msg: string); overload;
264266 procedure DebuglnThreadLog(const Msg: string); overload;
265267 procedure DebuglnThreadLog(Args: array of const); overload;
266268 procedure DebuglnThreadLog; overload;
267269```
268270
269271Zum Beispiel: Anstelle von *writeln('irgendein Text ', 123);* verwenden Sie
270272
271273``` pascal
272274 DebuglnThreadLog(['irgendein Text ', 123]);
273275```
274276
275277Dies wird eine Zeile 'irgendein Text 123' an die **Log\<PID\>.txt** Datei anhängen, wobei \<PID\> die Prozess ID des aktuellen Threads ist.
276278
277279Es ist eine gute Idee, die Logdateien vor jedem Lauf zu entfernen:
278280
279281``` bash
280282 rm -f Log* && ./project1
281283```
282284
283285### Linux
284286
285287Das Debuggen von grafischen Multithreading-Anwendungen unter Linux funktioniert mittlerweile problemlos. In der Vergangenheit traten aber aufgrund von Kompatibilitätsproblemen dabei Probleme mit dem X-Server auf: Er stürzte ab.
286288
287289Sollten bei Ihnen also solche Probleme auftreten, können Sie diese durch Starten einer neuen X-Instanz umgehen. Wie Sie dies erreichen, ist im Folgenden dargestellt. Eine andere Lösung ist bisher nicht bekannt.
288290
289291#### X-Server in einem neuen Terminal starten
290292
291293Starten Sie eine neue X-Instanz. Dies können Sie über die Konsole mit folgendem Befehl erreichen:
292294
293-` X :1 &`
295+```text
296+ X :1 &
297+```
294298
295299Nachdem Sie diesen Befehl ausgeführt haben, öffnet sich die neue X-Instanz. Mit den Tastenkombinationen \[Strg\]+\[Alt\]+\[F7\] und \[Strg\]+\[Alt\]+\[F8\] können Sie nun zwischen der Arbeitsoberfläche, auf der Sie arbeiten, und der neuen Instanz hin und her wechseln. (Bei der Slackware-Distribution und wenigen anderen wird dies durch die Kombination \[Strg\]+\[Alt\]+\[F2\] erreicht)
296300
297301Als nächsten Schritt müssen Sie eine Desktop-Sitzung in der neuen X-Instanz starten. Starten Sie zum Beispiel eine neue gnome-Sitzung in der Konsole:
298302
299-` gnome-session --display=:1 &`
303+```text
304+ gnome-session --display=:1 &
305+```
300306
301307#### X-Server in einem Fenster starten
302308
303309Mit Xnest oder Xephyr können Sie auch einen X-Server in einem Fenster eines anderen X-Servers betreiben. Der Vorteil ist, dass Sie beim Debuggen nicht mehr zwischen verschiedenen Terminals wechseln müssen.
304310
305311Mit dem Befehl
306312
307-` Xnest :1 -ac`
313+```text
314+ Xnest :1 -ac
315+```
308316
309317starten Sie mit Xnest eine X-Sitzung auf :1 und deaktivieren die Zugriffskontrolle.
310318
311319Unter GNOME können Sie auch folgenden Befehl verwenden:
312320
313-` gdmflexiserver --xnest`
321+```text
322+ gdmflexiserver --xnest
323+```
314324
315325Dies startet nicht nur eine X-Sitzung; es wird auch gleichzeitig eine neue GDM-Sitzung, in der Sie sich anmelden können, gestartet.
316326
317327#### Benötigte Einstellungen in Lazarus
318328
319329Schließlich müssen Sie eine letzte Einstellung in Lazarus selbst vornehmen: Gehen Sie in der IDE in das Menü "Start"-\>"Start-Parameter...". Aktivieren Sie dort das Häkchen "Display verwenden", und tragen Sie in das Feld darunter die Zuordnungsnummer ":1" ein.
320330
321331Jetzt wird die Anwendung auf dem zweiten X Server laufen und Sie sind in der Lage, sie vom ersten aus zu debuggen.
322332
323333Dies wurde mit Free Pascal 2.0 und Lazarus 0.9.10 unter Windows und Linux getestet.
324334
325335## Widgetsets
326336
327337Die win32, gtk und carbon Schnittstellen unterstützen Multithreading vollständig. Das bedeutet, TThread, critical sections und Synchronize funktioniert.
328338
329339## Critical sections
330340
331341Eine *critical section* (kritischer Abschnitt) stellt sicher, dass in einer multi-thread Anwendung nicht gleichzeitig von verschiedenen Threads aus auf einen bestimmten Bereich des Codes zugegriffen wird. Bevor eine critical section benutzt werden kann, muss sie erstellt und initialisiert werden; wenn sie nicht mehr gebraucht wird, muss sie freigegeben werden.
332342
333343Zur Verwendung von critical sections gehen Sie wie folgt vor:
334344
335345Fügen Sie über *uses* die Unit **SyncObjs** hinzu, die die notwendigen Routinen beinhaltet.
336346
337347Als nächsten Schritt deklarieren Sie die critical section (für alle Threads, die auf die section Zugriff haben sollen):
338348
339349``` pascal
340350 MyCriticalSection: TRTLCriticalSection;
341351```
342352
343353Erstellen Sie die Section mit folgender Prozedur:
344354
345-` InitCriticalSection(MyCriticalSection);`
355+```text
356+ InitCriticalSection(MyCriticalSection);
357+```
346358
347359Starten Sie einige Threads, die exklusive Aufgaben durchführen:
348360
349361``` pascal
350362 EnterCriticalSection(MyCriticalSection);
351363 try
352364 // An dieser Stelle können Sie auf Variablen zugreifen, Dateien schreiben, auf das Netzwerk zugreifen, u.s.w.
353365 finally
354366 LeaveCriticalSection(MyCriticalSection);
355367 end;
356368```
357369
358370Nachdem alle Threads, die Zugriff auf die critical section hatten, beendet wurden, müssen Sie sie nun deinitialisieren:
359371
360372``` pascal
361373 DoneCriticalSection(MyCriticalSection);
362374```
363375
364376Alternativ können Sie die Klasse TCriticalSection benutzen. Das Erstellen eines Objektes der Klasse entspricht hier der Initialisierung, die Methode Enter entspricht obigem EnterCriticalSection, die Methode Leave entspricht der obigen LeaveCriticalSection und die Vernichtung (destruction) des Objektes erledigt das Freigeben.
365377
366378Beispiel: 5 Threads, die einen einzigen Zähler erhöhen: lazarus/examples/multithreading/criticalsectionexample1.lpi
367379
368380**ACHTUNG:** Sowohl die RTL als auch die LCL beinhalten beide obige 4 Funktionen. (Die Funktionen der LCL sind in den Units LCLIntf und LCLType definiert.) Beide erfüllen den gleichen Zweck. Sie können sowohl die Funktionen der RTL als auch die Funktionen der LCL zur selben Zeit in Ihrem Programm einsetzen. Sie sollten es nur vermeiden, die "critical section"-Funktionen der LCL auf die Funktionen der RTL anzuwenden und umgekehrt.
369381
370382### Gemeinsamer Variablenzugriff
371383
372384Mehrere Threads können sich eine Variable im Lesezugriff teilen, ohne dass Probleme zu erwarten sind. Nur wenn der eine oder andere Thread den Wert der Variablen ändern möchte, muss sicher gestellt werden, dass niemals mehrere Threads gleichzeitig auf die Variable zugreifen.
373385
374386Als Beispiel: 5 Threads inkrementieren einen Counter: lazarus/examples/multithreading/criticalsectionexample1.lpi
375387
376388## Wenn ein Thread das Ergebnis eines anderen benötigt...
377389
378390Wenn ein Thread A das Ergebnis eines Threads B benötigt, muss er warten, bis B fertig ist.
379391
380392**Wichtig:** Der Main-Thread sollte niemals auf einen anderen Thread warten müssen. Für diesen Fall benutzen Sie am Besten Synchronize (oben beschrieben).
381393
382394Beispiel: lazarus/examples/multithreading/waitforexample1.lpi
383395
384396``` pascal
385397{ TThreadA }
386398
387399procedure TThreadA.Execute;
388400begin
389401 Form1.ThreadB := TThreadB.Create(false);
390402 // create event
391403 WaitForB := RTLEventCreate;
392404 while not Application.Terminated do begin
393405 // wait infinitely (until B wakes A)
394406 RtlEventWaitFor(WaitForB);
395407 WriteLn('A: ThreadB.Counter=', Form1.ThreadB.Counter);
396408 end;
397409end;
398410
399411{ TThreadB }
400412
401413procedure TThreadB.Execute;
402414var
403415 i: Integer;
404416begin
405417 Counter := 0;
406418 while not Application.Terminated do begin
407419 // B: Working ...
408420 Sleep(1500);
409421 inc(Counter);
410422 // wake A
411423 RtlEventSetEvent(Form1.ThreadA.WaitForB);
412424 end;
413425end;
414426```
415427
416428## Fork
417429
418430When forking in a multithreaded application, be aware that any threads created and running BEFORE the fork (or fpFork) call, will NOT be running in the child process. As stated on the fork() man page, any threads that were running before the fork call, their state will be undefined.
419431
420432So be aware of any threads initializing before the call (including on the initialization section). They will NOT work.
421433
422434## Distributed computing
423435
424436The next higher steps after multi threading is running the treads on multiple machines.
425437
426438- You can use one of the TCP suites like synapse, lnet or indy for communications. This gives you maximum flexibility and is mostly used for loosely connected Client / Server applications.
427439- You can use message passing libraries like [[MPICH]], which are used for HPC (High Performance Computing) on clusters.
428440
429441---
430442*From the Free Pascal wiki, page "Multithreaded Application Tutorial/de", as it stood on 2023-06-23 after a change by Monta, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).*

Back to history