Sign in Join
Wiki copytemporary

Multithreaded Application Tutorial/de: what changed

Revision 0 to 1. Removed lines have a minus sign; added lines have a plus sign.

Title: Multithreaded Application Tutorial/de

OldNewLine
1+\
2+Zurück zu den [[Additional information/de|Zusätzlichen Informationen]].\
3+\
4+== Überblick ==
5+
6+Diese 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.
7+
8+Wenn 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.
9+
10+Das 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.
11+
12+Andere Anwendungen, bei denen Multithread-Programmierung zum Einsatz kommt, sind Server-Anwendungen, die mehrere Klienten gleichzeitig betreuen müssen.
13+
14+Multithread-Anwendungen ermöglichen auch die Aufteilungen der Lasten einer Berechnung auf mehrere Kerne einer Multi-Core-CPU.
15+
16+Wichtig: 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).
17+
18+## Benötigt Ihre Anwendung wirklich Multithread-Eigenschaften?
19+
20+Wenn 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.
21+
22+Zum Beispiel: In den Lazarus-Beispielen unter examples/multithreading/singlethreadingexample1.lpi wird eine große Datei gelesen und verarbeitet und die oben genannte Technik benutzt.
23+
24+Multithreading wird wirklich benötigt für:
25+
26+- blocking handles, wie Netzwerkkommunikation, Interrupts (Linux)
27+- Mehrprozessor- oder Mehrkernbetrieb
28+- Algorithmen und Bibliotheksaufrufe, die nicht in mehrere kleine Stufen zerteilt werden können.
29+
30+## Die Klasse TThread
31+
32+Das 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".
33+
34+Ab 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.
35+
36+In die (mit Parameter Override) überschriebene Methode Execute schreibt man den vom Thread auszuführenden Quelltext hinein.
37+
38+Die Klasse TThread besitzt die wichtige Eigenschaft (property): Terminated : boolean; (Standardeinstellung: Terminated=false)
39+
40+Der 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.
41+
42+Wie 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.
43+
44+Um 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.
45+
46+Eine 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:
47+
48+``` pascal
49+ Type
50+ TMyThread = class(TThread)
51+ private
52+ fStatusText : string;
53+ procedure ShowStatus;
54+ protected
55+ procedure Execute; override;
56+ public
57+ Constructor Create(CreateSuspended : boolean);
58+ end;
59+
60+ constructor TMyThread.Create(CreateSuspended : boolean);
61+ begin
62+ FreeOnTerminate := True;
63+ inherited Create(CreateSuspended);
64+ end;
65+
66+ procedure TMyThread.ShowStatus;
67+ // Diese Methode wird vom MainThread ausgeführt und kann deshalb auf alle GUI-Elemente zugreifen
68+ begin
69+ Form1.Caption := fStatusText;
70+ end;
71+
72+ procedure TMyThread.Execute;
73+ var
74+ newStatus : string;
75+ begin
76+ fStatusText := 'TMyThread Starting...';
77+ Synchronize(@Showstatus);
78+ fStatusText := 'TMyThread Running...';
79+ while (not Terminated) and ([any condition required]) do
80+ begin
81+ ...
82+ // here goes the code of the main thread loop
83+ ...
84+ if NewStatus <> fStatusText then
85+ begin
86+ fStatusText := newStatus;
87+ Synchronize(@Showstatus);
88+ end;
89+ end;
90+ end;
91+```
92+
93+In der Hauptunit erfolgt dann folgender Aufruf
94+
95+``` pascal
96+ var
97+ MyThread : TMyThread;
98+ begin
99+ MyThread := TMyThread.Create(True); // So startet es nicht automatisch
100+ ...
101+ // Here the code initialises anything required before the threads starts executing
102+ ...
103+ MyThread.Resume;
104+ end;
105+```
106+
107+Soll 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:
108+
109+``` pascal
110+ Type
111+ TShowStatusEvent = procedure(Status: String) of Object;
112+
113+ TMyThread = class(TThread)
114+ private
115+ fStatusText : string;
116+ FOnShowStatus: TShowStatusEvent;
117+ procedure ShowStatus;
118+ protected
119+ procedure Execute; override;
120+ public
121+ Constructor Create(CreateSuspended : boolean);
122+ property OnShowStatus: TShowStatusEvent read FOnShowStatus write FOnShowStatus;
123+ end;
124+
125+ constructor TMyThread.Create(CreateSuspended : boolean);
126+ begin
127+ FreeOnTerminate := True;
128+ inherited Create(CreateSuspended);
129+ end;
130+
131+ procedure TMyThread.ShowStatus;
132+ // Diese Methode wird vom MainThread ausgeführt und kann deshalb auf alle GUI-Elemente zugreifen
133+ begin
134+ if Assigned(FOnShowStatus) then
135+ begin
136+ FOnShowStatus(fStatusText);
137+ end;
138+ end;
139+
140+ procedure TMyThread.Execute;
141+ var
142+ newStatus : string;
143+ begin
144+ fStatusText := 'TMyThread Starting...';
145+ Synchronize(@Showstatus);
146+ fStatusText := 'TMyThread Running...';
147+ while (not Terminated) and ([any condition required]) do
148+ begin
149+ ...
150+ [here goes the code of the main thread loop]
151+ ...
152+ if NewStatus <> fStatusText then
153+ begin
154+ fStatusText := newStatus;
155+ Synchronize(@Showstatus);
156+ end;
157+ end;
158+ end;
159+```
160+
161+Und im Hauptformular
162+
163+``` pascal
164+ Type
165+ TForm1 = class(TForm)
166+ Button1: TButton;
167+ Label1: TLabel;
168+ procedure FormCreate(Sender: TObject);
169+ procedure FormDestroy(Sender: TObject);
170+ private
171+ { private declarations }
172+ MyThread: TMyThread;
173+ procedure ShowStatus(Status: string);
174+ public
175+ { public declarations }
176+ end;
177+
178+ procedure TForm1.FormCreate(Sender: TObject);
179+ begin
180+ inherited;
181+ MyThread := TMyThread.Create(true);
182+ MyThread.OnShowStatus := @ShowStatus;
183+ end;
184+
185+ procedure TForm1.FormDestroy(Sender: TObject);
186+ begin
187+ MyThread.Terminate;
188+ MyThread.Free;
189+ inherited;
190+ end;
191+
192+ procedure TForm1.Button1Click(Sender: TObject);
193+ begin
194+ MyThread.Resume;
195+ end;
196+
197+ procedure TForm1.ShowStatus(Status: string);
198+ begin
199+ Label1.Caption := Status;
200+ end;
201+```
202+
203+## Worauf Sie achten sollten
204+
205+Wenn 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.
206+
207+Um auf Exceptions im Thread zu prüfen, können Sie folgendermaßen vorgehen:
208+
209+``` pascal
210+ MyThread := TThread.Create(False);
211+ if Assigned(MyThread.FatalException) then
212+ raise MyThread.FatalException;
213+```
214+
215+Dieser Code sorgt dafür daß Exceptions, die während der Erzeugung des Threads auftreten, im Main Thread einen Ausnahmefehler auslösen.
216+
217+## Für Multithread-Anwendungen benötigte Units
218+
219+Unter 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)!
220+
221+Daher sollte der Code Ihrer Lazarus-Anwendung etwa so aussehen:
222+
223+``` pascal
224+ program MyMultiThreadedProgram;
225+ {$mode objfpc}{$H+}
226+ uses
227+ {$ifdef unix}
228+ cthreads,
229+ {$endif}
230+ Interfaces, // dies bindet das LCL Widgetset ein
231+ Forms
232+ { fügen Sie Ihre Units hier hinzu },
233+```
234+
235+Wenn Sie dies vergessen, kommt die folgende Fehlermeldung:
236+
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+
240+Lazarus 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):
241+
242+``` pascal
243+ uses
244+ {$IFDEF UNIX}{$IFDEF UseCThreads}
245+ cthreads,
246+ {$ENDIF}{$ENDIF}
247+```
248+
249+## SMP (Mehrprozessor) Unterstützung
250+
251+Sobald Sie Threads benutzen, werden diese vom Betriebsystem auf mehrere Prozessoren verteilt.
252+
253+## Debuging Multithread-Anwendungen mit Lazarus
254+
255+Das Debuggen von Multithread-Anwendungen wird von Lazarus zur Zeit noch nicht vollständig unterstützt.
256+
257+### Debugger Ausgabe
258+
259+In 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.
260+
261+Die Unit LCLProc enthält einige Funktionen, um jeden Thread seine eigene Logdatei schreiben zu lassen:
262+
263+``` pascal
264+ procedure DbgOutThreadLog(const Msg: string); overload;
265+ procedure DebuglnThreadLog(const Msg: string); overload;
266+ procedure DebuglnThreadLog(Args: array of const); overload;
267+ procedure DebuglnThreadLog; overload;
268+```
269+
270+Zum Beispiel: Anstelle von *writeln('irgendein Text ', 123);* verwenden Sie
271+
272+``` pascal
273+ DebuglnThreadLog(['irgendein Text ', 123]);
274+```
275+
276+Dies wird eine Zeile 'irgendein Text 123' an die **Log\<PID\>.txt** Datei anhängen, wobei \<PID\> die Prozess ID des aktuellen Threads ist.
277+
278+Es ist eine gute Idee, die Logdateien vor jedem Lauf zu entfernen:
279+
280+``` bash
281+ rm -f Log* && ./project1
282+```
283+
284+### Linux
285+
286+Das 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.
287+
288+Sollten 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.
289+
290+#### X-Server in einem neuen Terminal starten
291+
292+Starten Sie eine neue X-Instanz. Dies können Sie über die Konsole mit folgendem Befehl erreichen:
293+
294+` X :1 &`
295+
296+Nachdem 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)
297+
298+Als 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:
299+
300+` gnome-session --display=:1 &`
301+
302+#### X-Server in einem Fenster starten
303+
304+Mit 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.
305+
306+Mit dem Befehl
307+
308+` Xnest :1 -ac`
309+
310+starten Sie mit Xnest eine X-Sitzung auf :1 und deaktivieren die Zugriffskontrolle.
311+
312+Unter GNOME können Sie auch folgenden Befehl verwenden:
313+
314+` gdmflexiserver --xnest`
315+
316+Dies startet nicht nur eine X-Sitzung; es wird auch gleichzeitig eine neue GDM-Sitzung, in der Sie sich anmelden können, gestartet.
317+
318+#### Benötigte Einstellungen in Lazarus
319+
320+Schließ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.
321+
322+Jetzt wird die Anwendung auf dem zweiten X Server laufen und Sie sind in der Lage, sie vom ersten aus zu debuggen.
323+
324+Dies wurde mit Free Pascal 2.0 und Lazarus 0.9.10 unter Windows und Linux getestet.
325+
326+## Widgetsets
327+
328+Die win32, gtk und carbon Schnittstellen unterstützen Multithreading vollständig. Das bedeutet, TThread, critical sections und Synchronize funktioniert.
329+
330+## Critical sections
331+
332+Eine *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.
333+
334+Zur Verwendung von critical sections gehen Sie wie folgt vor:
335+
336+Fügen Sie über *uses* die Unit **SyncObjs** hinzu, die die notwendigen Routinen beinhaltet.
337+
338+Als nächsten Schritt deklarieren Sie die critical section (für alle Threads, die auf die section Zugriff haben sollen):
339+
340+``` pascal
341+ MyCriticalSection: TRTLCriticalSection;
342+```
343+
344+Erstellen Sie die Section mit folgender Prozedur:
345+
346+` InitCriticalSection(MyCriticalSection);`
347+
348+Starten Sie einige Threads, die exklusive Aufgaben durchführen:
349+
350+``` pascal
351+ EnterCriticalSection(MyCriticalSection);
352+ try
353+ // An dieser Stelle können Sie auf Variablen zugreifen, Dateien schreiben, auf das Netzwerk zugreifen, u.s.w.
354+ finally
355+ LeaveCriticalSection(MyCriticalSection);
356+ end;
357+```
358+
359+Nachdem alle Threads, die Zugriff auf die critical section hatten, beendet wurden, müssen Sie sie nun deinitialisieren:
360+
361+``` pascal
362+ DoneCriticalSection(MyCriticalSection);
363+```
364+
365+Alternativ 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.
366+
367+Beispiel: 5 Threads, die einen einzigen Zähler erhöhen: lazarus/examples/multithreading/criticalsectionexample1.lpi
368+
369+**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.
370+
371+### Gemeinsamer Variablenzugriff
372+
373+Mehrere 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.
374+
375+Als Beispiel: 5 Threads inkrementieren einen Counter: lazarus/examples/multithreading/criticalsectionexample1.lpi
376+
377+## Wenn ein Thread das Ergebnis eines anderen benötigt...
378+
379+Wenn ein Thread A das Ergebnis eines Threads B benötigt, muss er warten, bis B fertig ist.
380+
381+**Wichtig:** Der Main-Thread sollte niemals auf einen anderen Thread warten müssen. Für diesen Fall benutzen Sie am Besten Synchronize (oben beschrieben).
382+
383+Beispiel: lazarus/examples/multithreading/waitforexample1.lpi
384+
385+``` pascal
386+{ TThreadA }
387+
388+procedure TThreadA.Execute;
389+begin
390+ Form1.ThreadB := TThreadB.Create(false);
391+ // create event
392+ WaitForB := RTLEventCreate;
393+ while not Application.Terminated do begin
394+ // wait infinitely (until B wakes A)
395+ RtlEventWaitFor(WaitForB);
396+ WriteLn('A: ThreadB.Counter=', Form1.ThreadB.Counter);
397+ end;
398+end;
399+
400+{ TThreadB }
401+
402+procedure TThreadB.Execute;
403+var
404+ i: Integer;
405+begin
406+ Counter := 0;
407+ while not Application.Terminated do begin
408+ // B: Working ...
409+ Sleep(1500);
410+ inc(Counter);
411+ // wake A
412+ RtlEventSetEvent(Form1.ThreadA.WaitForB);
413+ end;
414+end;
415+```
416+
417+## Fork
418+
419+When 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.
420+
421+So be aware of any threads initializing before the call (including on the initialization section). They will NOT work.
422+
423+## Distributed computing
424+
425+The next higher steps after multi threading is running the treads on multiple machines.
426+
427+- 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.
428+- You can use message passing libraries like [[MPICH]], which are used for HPC (High Performance Computing) on clusters.
429+
430+---
431+*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