Sign in Join
Wiki copytemporary

Multithreaded Application Tutorial: what changed

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

OldNewLine
11## Overview
22
33This page will try to explain how to write and debug a multi-threaded application with Free Pascal and Lazarus. A multi-threaded application is one that creates two or more threads of execution that work at the same time. If you are new to multi-threading, please read the paragraph "Do you need multi-threading?" to determine whether it is really required; this may save you many headaches.
44
55One of the threads is called the Main Thread. The Main Thread is the one that is created by the Operating System once our application starts. The Main Thread **must be** the only thread that updates the components that interfaces with the user: otherwise, the application may hang.
66
77The main idea is that the application can do some processing in background in a second thread while the user can continue working using the main thread.
88
99Another use of threads is just to have a better responding application. If you create an application, and when the user presses a button the application starts processing a big job... and while processing, the screen stops responding, and gives the user the impression that the application is frozen, a poor or misleading impression will be created. If the big job runs in a second thread, the application keeps responding (almost) as if it were idle. In this case it is a good idea, before starting the thread, to disable the buttons of the form to avoid the user starting more than one thread for the job.
1010
1111Another use of multi-threading may be a server application that is able to respond to many clients at the same time.
1212
1313## Do you need multi-threading?
1414
1515If you are new to multi-threading and you only want to make your application more responsive while your application performs moderately long-running tasks, then multi-threading may be more than is required. Multi-threaded applications are always more difficult to debug and they are often much more complex; in many cases you don't need multi-threading. A single thread is enough. If you can split up the time-consuming task into several smaller chunks, then instead you should use **Application.ProcessMessages**. This method allows the LCL to handle all waiting messages and returns. The central idea is to call Application.ProcessMessages at regular intervals during the execution of a long-running task to determine whether the user has clicked on something, or a progress indicator must be repainted, and so on.
1616
1717For example: Reading a big file and process it. See examples/multithreading/singlethreadingexample1.lpi.
1818
1919Multi-threading is only needed for
2020
2121- blocking handles, like network communications
2222- using multiple processors simultaneously (SMP)
2323- algorithms and library calls that must be called through an API and as such cannot be split up into smaller parts.
2424
2525If you want to use multi-threading to increase speed by using multiple processors simultaneously, check if your current program now uses all 100% resources of 1 core CPU (for example, your program can actively use input-output operations, e.g. writing to file; this takes a lot of time, but doesn't load CPU; in this case your program will not be faster with multiple threads). Also check if optimisation level is set to maximum (3). When switching optimisation level from 1 to 3, a program may become about 5 times faster.
2626
2727## Units needed for a multi-threaded application
2828
2929You don´t need any special unit for this to work with Windows. However with Linux, macOS and FreeBSD, you need the cthreads unit and it *must* be the first used unit of the project (the program source, usually the .lpr file)! In cases where several units like cthreads, cmem and cwstrings are recommended to be placed first, due to how the units work a sensible order is cmem, cthreads and then cwstrings.
3030
3131So, your Lazarus application code should look like:
3232
3333``` pascal
3434program MyMultiThreadedProgram;
3535{$mode objfpc}{$H+}
3636uses
3737{$ifdef unix}
3838 cthreads,
3939 cmem, // the c memory manager is on some systems much faster for multi-threading
4040{$endif}
4141 Interfaces, // this includes the LCL widgetset
4242 Forms
4343 { you can add units here },
4444```
4545
4646If you forget this and you use TThread you will get this error on startup:
4747
48-` This binary has no thread support compiled in.`\
49-` Recompile the application with a thread-driver in the program uses clause before other units using thread.`
48+``` pascal
49+ This binary has no thread support compiled in.
50+ Recompile the application with a thread-driver in the program uses clause before other units using thread.
51+```
5052
5153> **Note:** If you get a Linker error about "mcount" not found. Then you use some unit that contains some multithreaded code and you need to add the cthreads unit or use [[smart linking]].
5254
5355> **Note:** If you get the error: "Project raised exception class 'RunError(232)'" in procedure SYSTEM_NOTHREADERROR then your code requires threading and you need to add the cthreads unit.
5456
5557## Pure FPC example
5658
5759Below code gives a very simple example. Tested with FPC 3.0.4 on Win7.
5860
5961``` pascal
6062Program ThreadTest;
6163{test multi threading capability }
6264{
6365 OUTPUT
6466thread 1 started
6567thread 1 thri 0 Len(S)= 1
6668thread 1 thri 1 Len(S)= 2
6769thread 1 thri 2 Len(S)= 3
6870thread 1 thri 3 Len(S)= 4
6971thread 1 thri 4 Len(S)= 5
7072thread 1 thri 5 Len(S)= 6
7173thread 1 thri 6 Len(S)= 7
7274thread 1 thri 7 Len(S)= 8
7375thread 1 thri 8 Len(S)= 9
7476thread 1 thri 9 Len(S)= 10
7577thread 1 thri 10 Len(S)= 11
7678thread 1 thri 11 Len(S)= 12
7779thread 1 thri 12 Len(S)= 13
7880thread 1 thri 13 Len(S)= 14
7981thread 1 thri 14 Len(S)= 15
8082thread 2 started
8183thread 3 started
8284thread 1 thri 15 Len(S)= 16
8385thread 2 thri 0 Len(S)= 1
8486thread 3 thri 0 Len(S)= 1
8587thread 1 thri 16 Len(S)= 17
8688...
8789...
8890thread 5 thri 997 Len(S)= 998
8991thread 5 thri 998 Len(S)= 999
9092thread 5 thri 999 Len(S)= 1000
9193thread 5 finished
9294thread 10 thri 828 Len(S)= 829
9395thread 9 thri 675 Len(S)= 676
9496thread 4 thri 656 Len(S)= 657
9597thread 10 thri 829 Len(S)= 830
9698thread 9 thri 676 Len(S)= 677
9799thread 9 thri 677 Len(S)= 678
98100thread 10 thri 830 Len(S)= 831
99101thread 10 thri 831 Len(S)= 832
100102thread 10 thri 832 Len(S)= 833
101103thread 10 thri 833 Len(S)= 834
102104thread 10 thri 834 Len(S)= 835
103105thread 10 thri 835 Len(S)= 836
104106thread 10 thri 836 Len(S)= 837
105107thread 10 thri 837 Len(S)= 838
106108thread 10 thri 838 Len(S)= 839
107109thread 10 thri 839 Len(S)= 840
108110thread 9 thri 678 Len(S)= 679
109111...
110112...
111113thread 4 thri 994 Len(S)= 995
112114thread 4 thri 995 Len(S)= 996
113115thread 4 thri 996 Len(S)= 997
114116thread 4 thri 997 Len(S)= 998
115117thread 4 thri 998 Len(S)= 999
116118thread 4 thri 999 Len(S)= 1000
117119thread 4 finished
11812010
119121
120122}
121123
122124uses
123125 {$ifdef unix}cthreads, {$endif} sysutils;
124126
125127const
126128 threadcount = 10;
127129 stringlen = 1000;
128130
129131var
130132 finished : longint;
131133
132134threadvar
133135 thri : ptrint;
134136
135137function f(p : pointer) : ptrint;
136138var
137139 s : ansistring;
138140begin
139141 Writeln('thread ',longint(p),' started');
140142 thri:=0;
141143 while (thri<stringlen) do begin
142144 s:=s+'1'; { create a delay }
143145 writeln('thread ',longint(p),' thri ',thri,' Len(S)= ',length(s));
144146 inc(thri);
145147 end;
146148 Writeln('thread ',longint(p),' finished');
147149 InterLockedIncrement(finished);
148150 f:=0;
149151end;
150152
151153var
152154 i : longint;
153155
154156Begin
155157 finished:=0;
156158 for i:=1 to threadcount do
157159 BeginThread(@f,pointer(i));
158160 while finished<threadcount do ;
159161 Writeln(finished);
160162End.
161163```
162164
163165## The TThread Class
164166
165167The following example can be found in the examples/multithreading/ directory.
166168
167169To create a multi-threaded application, the easiest way is to use the TThread Class. This class permits the creation of an additional thread (alongside the main thread) in a simple way. Normally you are required to override only 2 methods: the Create constructor, and the Execute method.
168170
169171In the constructor, you will prepare the thread to run. You will set the initial values of the variables or properties you need. The original constructor of TThread requires a parameter called Suspended. As you might expect, setting Suspended = True will prevent the thread starting automatically after the creation. If Suspended = False, the thread will start running just after the creation. If the thread is created suspended, then it will run only after the Start method is called.
170172
171173> **Note:** Method Resume is [deprecated since FPC 2.4.4](http://wiki.freepascal.org/User_Changes_2.4.4#TThread.Suspend_and_TThread.Resume_have_been_deprecated). It is replaced by Start.
172174
173175As of FPC version 2.0.1 and later, TThread.Create also has an implicit parameter for Stack Size. You can now change the default stack size of each thread you create if you need it. Deep procedure call recursions in a thread are a good example. If you don't specify the stack size parameter, a default OS stack size is used.
174176
175177In the overridden Execute method you will write the code that will run on the thread.
176178
177179The TThread class has one important property: Terminated : boolean;
178180
179181If the thread has a loop (and this is typical), the loop should be exited when Terminated is true (it is false by default). Within each pass, the value of Terminated must be checked, and if it is true then the loop should be exited as quickly as is appropriate, after any necessary cleanup. ~~Bear in mind that the Terminate method does not do anything by default: the .Execute method must explicitly implement support for it to quit its job.~~ The Terminate method only sets the Terminated property to True.
180182
181183As we explained earlier, the thread should not interact with the visible components. Updates to visible components must be made within the context of the main thread.
182184
183185To do this, a TThread method called Synchronize exists. Synchronize requires a method within the thread (that takes no parameters) as an argument. When you call that method through Synchronize(@MyMethod), the thread execution will be paused, the code of MyMethod will be called from the main thread, and then the thread execution will be resumed.
184186
185187The exact working of Synchronize depends on the platform, but basically it does this:
186188
187189- it posts a message onto the main message queue and goes to sleep
188190- eventually the main thread processes the message and calls MyMethod. This way MyMethod is called without context, that means not during a mouse down event or during paint event, but after.
189191- after the main thread executed MyMethod, it wakes the sleeping Thread and processes the next message
190192- the Thread then continues.
191193
192194There is another important property of TThread: FreeOnTerminate. If this property is true, the thread object is automatically freed when the thread execution (.Execute method) stops. Otherwise the application will need to free it manually.
193195
194196Example:
195197
196198``` pascal
197199 Type
198200 TMyThread = class(TThread)
199201 private
200202 fStatusText : string;
201203 procedure ShowStatus;
202204 protected
203205 procedure Execute; override;
204206 public
205207 Constructor Create(CreateSuspended : boolean);
206208 end;
207209 constructor TMyThread.Create(CreateSuspended : boolean);
208210 begin
209211 inherited Create(CreateSuspended);
210212 FreeOnTerminate := True;
211213 end;
212214
213215 procedure TMyThread.ShowStatus;
214216 // this method is executed by the mainthread and can therefore access all GUI elements.
215217 begin
216218 Form1.Caption := fStatusText;
217219 end;
218220
219221 procedure TMyThread.Execute;
220222 var
221223 newStatus : string;
222224 begin
223225 fStatusText := 'TMyThread Starting...';
224226 Synchronize(@Showstatus);
225227 fStatusText := 'TMyThread Running...';
226228 while (not Terminated) and ([any condition required]) do
227229 begin
228230 ...
229231 [here goes the code of the main thread loop]
230232 ...
231233 if NewStatus <> fStatusText then
232234 begin
233235 fStatusText := newStatus;
234236 Synchronize(@Showstatus);
235237 end;
236238 end;
237239 end;
238240```
239241
240242In the application:
241243
242244``` pascal
243245 var
244246 MyThread : TMyThread;
245247 begin
246248 MyThread := TMyThread.Create(True); // This way it doesn't start automatically
247249 ...
248250 [Here the code initialises anything required before the threads starts executing]
249251 ...
250252 MyThread.Start;
251253 end;
252254```
253255
254256If you want to make your application more flexible you can create an event for the thread; this way your synchronized method won't be tightly coupled with a specific form or class: you can attach listeners to the thread's event. Here is an example:
255257
256258``` pascal
257259 Type
258260 TShowStatusEvent = procedure(Status: String) of Object;
259261
260262 TMyThread = class(TThread)
261263 private
262264 fStatusText : string;
263265 FOnShowStatus: TShowStatusEvent;
264266 procedure ShowStatus;
265267 protected
266268 procedure Execute; override;
267269 public
268270 Constructor Create(CreateSuspended : boolean);
269271 property OnShowStatus: TShowStatusEvent read FOnShowStatus write FOnShowStatus;
270272 end;
271273
272274 constructor TMyThread.Create(CreateSuspended : boolean);
273275 begin
274276 inherited Create(CreateSuspended);
275277 FreeOnTerminate := True;
276278 end;
277279
278280 procedure TMyThread.ShowStatus;
279281 // this method is executed by the mainthread and can therefore access all GUI elements.
280282 begin
281283 if Assigned(FOnShowStatus) then
282284 begin
283285 FOnShowStatus(fStatusText);
284286 end;
285287 end;
286288
287289 procedure TMyThread.Execute;
288290 var
289291 newStatus : string;
290292 begin
291293 fStatusText := 'TMyThread Starting...';
292294 Synchronize(@Showstatus);
293295 fStatusText := 'TMyThread Running...';
294296 while (not Terminated) and ([any condition required]) do
295297 begin
296298 ...
297299 [here goes the code of the main thread loop]
298300 ...
299301 if NewStatus <> fStatusText then
300302 begin
301303 fStatusText := newStatus;
302304 Synchronize(@Showstatus);
303305 end;
304306 end;
305307 end;
306308```
307309
308310In the application:
309311
310312``` pascal
311313 Type
312314 TForm1 = class(TForm)
313315 Button1: TButton;
314316 Label1: TLabel;
315317 procedure FormCreate(Sender: TObject);
316318 procedure FormDestroy(Sender: TObject);
317319 private
318320 { private declarations }
319321 MyThread: TMyThread;
320322 procedure ShowStatus(Status: string);
321323 public
322324 { public declarations }
323325 end;
324326
325327 procedure TForm1.FormCreate(Sender: TObject);
326328 begin
327329 inherited;
328330 MyThread := TMyThread.Create(true);
329331 MyThread.OnShowStatus := @ShowStatus;
330332 end;
331333
332334 procedure TForm1.FormDestroy(Sender: TObject);
333335 begin
334336 MyThread.Terminate;
335337
336338 // FreeOnTerminate is true so we should not write:
337339 // MyThread.Free;
338340 inherited;
339341 end;
340342
341343 procedure TForm1.Button1Click(Sender: TObject);
342344 begin
343345 MyThread.Start;
344346 end;
345347
346348 procedure TForm1.ShowStatus(Status: string);
347349 begin
348350 Label1.Caption := Status;
349351 end;
350352```
351353
352354## Special things to take care of
353355
354356### Stack checking under Windows
355357
356358There is a potential headache in Windows with Threads if you use the -Ct (stack check) switch. For reasons not so clear the stack check will "trigger" on any TThread.Create if you use the default stack size. The only work-around for the moment is to simply not use -Ct switch. Note that it does NOT cause an exception in the main thread, but in the newly created one. This "looks" like if the thread was never started.
357359
358360A good code to check for this and other exceptions which can occur in thread creation is:
359361
360362``` pascal
361363MyThread := TThread.Create(False);
362364
363365if Assigned(MyThread.FatalException) then
364366 raise MyThread.FatalException;
365367```
366368
367369This code will assure that any exception which occurred during thread creation will be raised in your main thread.
368370
369371### GetDC function in Windows
370372
371373The GetDC (and GetDCEx) function retrieves a handle to a device context (DC) for the client area of a specified window or for the entire screen. It can be used, for example, to [[Developing with Graphics|take a screenshot]]. Note however that it is not thread safe: the handle to the DC can only be used by a single thread at any one time as [documented here](https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getdc).
372374
373375### Multithreading in packages
374376
375377Packages which uses multi-threading should add the **-dUseCThreads** flag to the custom usage options. Open the package editor of the package, then Options \> Usage \> Custom and add *-dUseCThreads*. This will define this flag to all projects and packages using this package, including the IDE. The IDE and all new applications created by the IDE have already the following code in their .lpr file:
376378
377379``` pascal
378380uses
379381 {$IFDEF UNIX}{$IFDEF UseCThreads}
380382 cthreads,
381383 cmem, // the c memory manager is on some systems much faster for multi-threading
382384 {$ENDIF}{$ENDIF}
383385```
384386
385387### Heaptrc
386388
387389You can not use the -gh switch with the *cmem* unit. The -gh switch uses the heaptrc unit, which extends the heap manager. Therefore the **heaptrc** unit must be used **after** the **cmem** unit.
388390
389391``` pascal
390392uses
391393 {$IFDEF UNIX}{$IFDEF UseCThreads}
392394 cthreads,
393395 cmem, // the c memory manager is on some systems much faster for multi-threading
394396 {$ENDIF}{$ENDIF}
395397 heaptrc,
396398```
397399
398400### Initialization and Finalization
399401
400402To initialize the thread object itself, you can either start it suspended and set its properties and/or create a new constructor and call the inherited constructor.
401403
402404**Note**: Using the AfterConstruction when CreateSuspended=false is dangerous, as the thread has already started.
403405
404406On the other hand, the destructor may be used to finalize the object's resources.
405407
406408``` pascal
407409type
408410 TMyThread = class(TThread)
409411 private
410412 fRTLEvent: PRTLEvent;
411413 public
412414 constructor Create(SomeData: TSomeObject; CreateSuspended: boolean);
413415 destructor Destroy; override;
414416 end;
415417
416418constructor TMyThread.Create(SomeData: TSomeObject; CreateSuspended: boolean);
417419begin
418420 // example: set up events, critical sections and other ressources like files or database connections
419421 RTLEventCreate(fRTLEvent);
420422 inherited Create(CreateSuspended);
421423end;
422424
423425destructor TMyThread.Destroy;
424426begin
425427 RTLeventDestroy(fRTLEvent);
426428 inherited Destroy;
427429end;
428430```
429431
430432### Non LCL program
431433
432434TThread.Synchronize requires that the main thread regularly calls CheckSynchronize. The LCL does that in its loop. If you don't use the LCL event loop you must call it yourself.
433435
434436## SMP Support
435437
436438The good news is that if your application works properly multi-threaded this way, it is already SMP enabled!
437439
438440## Debugging Multi-threaded Applications with Lazarus
439441
440442The debugging on Lazarus requires GDB and is rapidly becoming more and more fully featured and stable. However, there still exists a few Linux distributions with some problems.
441443
442444### Debugging output
443445
444446In a single threaded application, you can simply write to console/terminal/whatever and the order of the lines is the same as they were written. In multi-threaded application things are more complicated. If two threads are writing, say a line is written by thread A before a line by thread B, then the lines are not necessarily written in that order. It can even happen, that a thread writes its output, while the other thread is writing a line.While under linux (maybe) you'll get proper [[DebugLn]]() output, under win32 you can get exceptions (probably DiskFull) because of DebugLn() usage outside of main thread.So, to avoid headaches use DebugLnThreadLog() mentioned below.
445447
446448The LCLProc unit contains several functions, to let each thread write to its own log file:
447449
448450``` pascal
449451 procedure DbgOutThreadLog(const Msg: string); overload;
450452 procedure DebuglnThreadLog(const Msg: string); overload;
451453 procedure DebuglnThreadLog(Args: array of const); overload;
452454 procedure DebuglnThreadLog; overload;
453455```
454456
455457For example: Instead of *writeln('Some text ',123);* use
456458
457-` DebuglnThreadLog(['Some text ',123]);`
459+```text
460+ DebuglnThreadLog(['Some text ',123]);
461+```
458462
459463This will append a line 'Some text 123' to **Log\<PID\>.txt**, where \<PID\> is the process ID of the current thread.
460464
461465It is a good idea to remove the log files before each run:
462466
463-` rm -f Log* && ./project1`
467+```text
468+ rm -f Log* && ./project1
469+```
464470
465471### Linux
466472
467473If you try to debug a multi-threaded application on Linux, you will have one big problem: the Desktop Manager on X server can hang. This happens for instance when the application has captured the mouse/keyboard and was paused by gdb and the X server waits for your application. When that happens you can simply log in from another computer and kill the gdb or exit out of that session by pressing CTRL+ALT+F3 and kill gdb. Alternatively you can restart the window manager: enter sudo /etc/init.d/gdm restart. This will restart the desktop manager and get you back into your desktop.
468474
469475Since it depends where gdb stops your program in some cases some tricks may help: for Ubuntu x64 set the Project options for debugging required extra information file...
470476
471-` Project Options -> Compiler Options -> Linking -> Debugging: Check Use external gdb debug symbols file (-Xg).`
477+```text
478+ Project Options -> Compiler Options -> Linking -> Debugging: Check Use external gdb debug symbols file (-Xg).
479+```
472480
473481The other option is to open another X desktop, run the IDE/gdb on one and the application on the other, so that only the test desktop freezes. Create a new instance of X with:
474482
475-` X :1 &`
483+```text
484+ X :1 &
485+```
476486
477487It will open, and when you switch to another desktop (the one you are working with pressing CTRL+ALT+F7), you will be able to go back to the new graphical desktop with CTRL+ALT+F8 (if this combination does not work, try with CTRL+ALT+F2... this one worked on [Slackware](http://www.slackware.com)).
478488
479489Then you could, if you want, create a desktop session on the X started with:
480490
481-` gnome-session --display=:1 &`
491+```text
492+ gnome-session --display=:1 &
493+```
482494
483495Then, in Lazarus, on the run parameters dialog for the project, check "Use display" and enter :1.
484496
485497Now the application will run on the second X server and you will be able to debug it on the first one.
486498
487499This was tested with Free Pascal 2.0 and Lazarus 0.9.10 on Windows and Linux.
488500
489501------------------------------------------------------------------------
490502
491503Instead of creating a new X session, one can use [Xnest](http://en.wikipedia.org/wiki/Xnest). Xnest is a X session on a window. Using it X server didn't lock while debugging threads, and it's much easier to debug without keeping changing terminals.
492504
493505The command line to run Xnest is
494506
495-` Xnest :1 -ac`
507+```text
508+ Xnest :1 -ac
509+```
496510
497511to create a X session on :1, and disabling access control.
498512
499513## Lazarus Widgetset Interfaces
500514
501515The win32, the gtk and the carbon interfaces support multi-threading. This means, TThread, critical sections and Synchronize work. But they are not thread safe. This means only one thread at a time can access the LCL. And since the main thread should never wait for another thread, it means only the main thread is allowed to access the LCL, which means anything that has to do with TControl, Application and LCL widget handles. There are some thread safe functions in the LCL. For example most of the functions in the FileUtil unit are thread safe.
502516
503517### Using SendMessage/PostMessage to communicate between threads
504518
505519Only one thread in an application should call LCL APIs, usually the main thread. Other threads can make use of the LCL through a number of indirect methods, one good option being the usage of SendMessage or PostMessage. LCLIntf.SendMessage and LCLIntf.PostMessage will post a message directed to a window in the message pool of the application.
506520
507521See also the documentation for these routines:
508522
509523- [[doc:lcl/lclintf/sendmessage.html|SendMessage]]
510524- [[doc:lcl/lclintf/postmessage.html|PostMessage]]
511525
512526The difference between SendMessage and PostMessage is the way that they return control to the calling thread. Like Synchronize, SendMessage blocks and control is not returned until the window that the message was sent to has completed processing it; however under certain circumstances SendMessage might attempt to optimise processing by remaining in the context of the thread that called it. With PostMessage control is returned immediately up to some system-defined maximum number of enqueued messages and as long as space remains on the heap for attached data.
513527
514528In both cases the procedure handling the message (see below) should avoid calling application.ProcessMessages, since this might cause a second message to be dispatched which will be handled reentrantly. If this is unavoidable then it would probably be preferable to use some other mechanism to transfer serialised events between threads.
515529
516530Here is an example of how a secondary thread could send text to be displayed in an LCL control to the main thread:
517531
518532``` pascal
519533const
520534 WM_GOT_ERROR = LM_USER + 2004;
521535 WM_VERBOSE = LM_USER + 2005;
522536
523537procedure VerboseLog(Msg: string);
524538var
525539 PError: PChar;
526540begin
527541 if MessageHandler = 0 then Exit;
528542 PError := StrAlloc(Length(Msg)+1);
529543 StrCopy(PError, PChar(Msg));
530544 PostMessage(formConsole.Handle, WM_VERBOSE, Integer(PError), 0);
531545end;
532546```
533547
534548And an example of how to handle this message from a window:
535549
536550``` pascal
537551const
538552 WM_GOT_ERROR = LM_USER + 2004;
539553 WM_VERBOSE = LM_USER + 2005;
540554
541555type
542556 { TformConsole }
543557
544558 TformConsole = class(TForm)
545559 DebugList: TListView;
546560 // ...
547561 private
548562 procedure HandleDebug(var Msg: TLMessage); message WM_VERBOSE;
549563 end;
550564
551565var
552566 formConsole: TformConsole;
553567
554568implementation
555569
556570....
557571
558572{ TformConsole }
559573
560574procedure TformConsole.HandleDebug(var Msg: TLMessage);
561575var
562576 Item: TListItem;
563577 MsgStr: PChar;
564578 MsgPasStr: string;
565579begin
566580 MsgStr := PChar(Msg.wparam);
567581 MsgPasStr := StrPas(MsgStr);
568582 Item := DebugList.Items.Add;
569583 Item.Caption := TimeToStr(SysUtils.Now);
570584 Item.SubItems.Add(MsgPasStr);
571585 Item.MakeVisible(False);
572586
573587// Followed by something like
574588
575589 TrayControl.SetError(MsgPasStr);
576590 StrDispose(MsgStr)
577591end;
578592
579593end.
580594```
581595
582596When you get on the Linux x64 machine an error: "Project XY raised exception class 'External: SIGSEGV'" you have to change "Integer(PError)" to "PtrInt(PError)" in procedure "VerboseLog". Modified line have to look like:
583597
584-` PostMessage(formConsole.Handle, WM_VERBOSE, PtrInt(PError), 0);`
598+```text
599+ PostMessage(formConsole.Handle, WM_VERBOSE, PtrInt(PError), 0);
600+```
585601
586602## Critical sections
587603
588604A *critical section* is an object used to make sure, that some part of the code is executed only by one thread at a time. A critical section needs to be created/initialized before it can be used and be freed when it is not needed anymore.
589605
590606There are two ways you use critical sections, from the RTL or from LCL. The text below shows the RTL model and shows the LCL alternatives as a comment, the only difference is the method names.
591607
592608Critical sections are normally used this way:
593609
594610``` pascal
595611// uses LCLIntf, LCLType;
596612```
597613
598614the necessary RTL units will almost certainly already be mentioned.
599615
600616Declare the section (globally for all threads which should access the section):
601617
602618``` pascal
603619MyCriticalSection: TRTLCriticalSection;
604620// MyCriticalSection : TCriticalSection;
605621```
606622
607623Create the section:
608624
609625``` pascal
610626InitCriticalSection(MyCriticalSection);
611627//InitializeCriticalSection(MyCriticalSection);
612628```
613629
614630Run some threads. Doing something exclusively:
615631
616632``` pascal
617633EnterCriticalSection(MyCriticalSection);
618634try
619635 // access some variables, write files, send some network packets, etc
620636finally
621637 LeaveCriticalSection(MyCriticalSection);
622638end;
623639// RTL and LCL use same syntax, thats nice.
624640```
625641
626642After all threads terminated, free it:
627643
628644``` pascal
629645DoneCriticalSection(MyCriticalSection);
630646// DeleteCriticalSection(MyCriticalSection);
631647```
632648
633649As an alternative, you can use a TCriticalSection object. The creation does the initialization, the Enter method does the EnterCriticalSection, the Leave method does the LeaveCriticalSection and the destruction of the object does the deletion.
634650
635651Note that a critical section does not protect against the same thread entering the same block of code, only against different threads. For that reason it cannot be used to protect against e.g. reentry of a message handler (see the section above).
636652
637653For example: 5 threads incrementing a counter. See lazarus/examples/multithreading/criticalsectionexample1.lpi
638654
639655> **Warning:** There are two sets of the above four functions. The RTL and the LCL ones. The LCL ones are defined in the unit LCLIntf and LCLType. Both work pretty much the same. You can use both at the same time in your application, but you should not use a RTL function within an LCL Critical Section and vice versa.
640656
641657### Sharing Variables
642658
643659If some threads share a variable, that is read only, then there is nothing to worry about. Just read it. But if one or several threads changes the variable, then you must make sure, that only one thread accesses the variables at a time.
644660
645661For example: 5 threads incrementing a counter. See lazarus/examples/multithreading/criticalsectionexample1.lpi
646662
647663## Waiting for another thread
648664
649665If a thread A needs a result of another thread B, it must wait, till B has finished.
650666
651667**Important:** The main thread should never wait for another thread. Instead use Synchronize (see above).
652668
653669See for an example: lazarus/examples/multithreading/waitforexample1.lpi
654670
655671``` pascal
656672{ TThreadA }
657673
658674procedure TThreadA.Execute;
659675begin
660676 Form1.ThreadB:=TThreadB.Create(false);
661677 // create event
662678 WaitForB:=RTLEventCreate;
663679 while not Application.Terminated do begin
664680 // wait infinitely (until B wakes A)
665681 RtlEventWaitFor(WaitForB);
666682 writeln('A: ThreadB.Counter='+IntToStr(Form1.ThreadB.Counter));
667683 end;
668684end;
669685
670686{ TThreadB }
671687
672688procedure TThreadB.Execute;
673689var
674690 i: Integer;
675691begin
676692 Counter:=0;
677693 while not Application.Terminated do begin
678694 // B: Working ...
679695 Sleep(1500);
680696 inc(Counter);
681697 // wake A
682698 RtlEventSetEvent(Form1.ThreadA.WaitForB);
683699 end;
684700end;
685701```
686702
687703> **Note:** RtlEventSetEvent can be called before RtlEventWaitFor. Then RtlEventWaitFor will return immediately. Use RTLeventResetEvent to clear a flag.
688704
689705## Fork
690706
691707When forking in a multi-threaded 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.
692708
693709So be aware of any threads initializing before the call (including on the initialization section). They will NOT work.
694710
695711## Parallel procedures/loops
696712
697713A special case of multi threading is running a single procedure in parallel. See [[Parallel procedures]].
698714
699715## Distributed computing
700716
701717The next higher steps after multi threading is running the threads on multiple machines.
702718
703719- 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.
704720- You can use message passing libraries like [[MPICH]], which are used for HPC (High Performance Computing) on clusters.
705721
706722## External threads
707723
708724To make Free Pascal's threading system work properly, each newly created FPC thread needs to be initialized (more exactly, the exception, I/O system and threadvar system per thread needs to be initialized so threadvars and heap are working). That is fully automatically done for you if you use BeginThread (or indirectly by using the TThread class). However, if you use threads that were created without BeginThread (i.e. external threads), additional work (currently) might be required. External threads also include those that were created in external C libraries (.DLL/.so).
709725
710726Things to consider when using external threads (might not be needed in all or future compiler versions):
711727
712728- Do not use external threads at all - use FPC threads. If can you can get control over how the thread is created, create the thread by yourself by using BeginThread.
713729
714730If the calling convention doesn't fit (e.g. if your original thread function needs cdecl calling convention but BeginThread needs pascal convention, create a record, store the original required thread function in it, and call that function in your pascal thread function:
715731
716732``` pascal
717733type
718734 TCdeclThreadFunc = function (user_data:Pointer):Pointer;cdecl;
719735
720736 PCdeclThreadFuncData = ^TCdeclThreadFuncData;
721737 TCdeclThreadFuncData = record
722738 Func: TCdeclThreadFunc; //cdecl function
723739 Data: Pointer; //original data
724740 end;
725741
726742// The Pascal thread calls the cdecl function
727743function C2P_Translator(FuncData: pointer) : ptrint;
728744var
729745 ThreadData: TCdeclThreadFuncData;
730746begin
731747 ThreadData := PCdeclThreadFuncData(FuncData)^;
732748 Result := ptrint(ThreadData.Func(ThreadData.Data));
733749end;
734750
735751procedure CreatePascalThread;
736752var
737753 ThreadData: PCdeclThreadFuncData;
738754begin
739755 New(ThreadData);
740756 // this is the desired cdecl thread function
741757 ThreadData^.Func := func;
742758 ThreadData^.Data := user_data;
743759 // this creates the Pascal thread
744760 BeginThread(@C2P_Translator, ThreadData );
745761end;
746762```
747763
748764- Initialize the FPC's threading system by creating a dummy thread. If you don't create any Pascal thread in your app, the thread system won't be initialized (and thus threadvars won't work and thus heap will not work correctly).
749765
750766``` pascal
751767type
752768 tc = class(tthread)
753769 procedure execute;override;
754770 end;
755771
756772 procedure tc.execute;
757773 begin
758774 end;
759775
760776{ main program }
761777begin
762778 { initialise threading system }
763779 with tc.create(false) do
764780 begin
765781 waitfor;
766782 free;
767783 end;
768784 { ... your code follows }
769785end.
770786```
771787
772788(After the threading system is initialized, the runtime may set the system variable "IsMultiThread" to true which is used by FPC routines to perform locks here and there. You should not set this variable manually.)
773789
774790- If for some reason this doesn't work for you, try this code in your external thread function:
775791
776792``` pascal
777793function ExternalThread(param: Pointer): LongInt; stdcall;
778794var
779795 tm: TThreadManager;
780796begin
781797 GetThreadManager(tm);
782798 tm.AllocateThreadVars;
783799 InitThread(1000000); // adjust inital stack size here
784800
785801 { do something threaded here ... }
786802
787803 Result:=0;
788804end;
789805```
790806
791807### Identifying external threads
792808
793809Sometimes you even don't know if you have to deal with external threads (eg if some C library makes a [[callback]]). This can help to analyse this:
794810
7958111\. Ask the OS for the ID of the current thread at your application's start
796812
797813``` pascal
798814GetCurrentThreadID() // Windows;
799815GetThreadID() // Darwin (macOS); FreeBSD;
800816TThreadID(pthread_self) // Linux;
801817```
802818
8038192\. Ask again for the ID of the current thread inside the thread function and compare this with the result of step 1.
804820
805821### Give up some time slice
806822
807823[ThreadSwitch](https://www.freepascal.org/docs-html/rtl/system/threadswitch.html) - Note: may be ignored.
808824
809825> **Note:** Do not use the Windows trick [*Sleep(0)*](https://www.freepascal.org/docs-html/rtl/sysutils/sleep.html) as this won't work on all platforms.
810826
811827## See also
812828
813829- [[Streaming components]]
814830- [[Manager Worker Threads System]]
815831- [[Example of multi-threaded application: array of threads]]
816832- [[Parallel procedures]]
817833- [[Main Loop Hooks]]
818834- [[Asynchronous Calls]]
819835- [A forum discussion](https://forum.lazarus.freepascal.org/index.php/topic,55164.0.html) - useful background, why you should almost certainly not access LCL items from within a thread and what you can, safely access.
820836- [Mutlithreading - The Delphi way](http://nickhodges.com/MultiThreadingInDelphi/ToC.html)
821837- [Synchronizing Threads and GUI in a Delphi Application](https://www.thoughtco.com/synchronizing-threads-and-gui-delphi-application-1058159)
822838- [Introduction to thread programming in Lazarus](https://www.freepascal.org/~michael/articles/lazthread/lazthread.pdf) - handout given by Michael Van Canneyt
823839
824840---
825841*From the Free Pascal wiki, page "Multithreaded Application Tutorial", as it stood on 2024-02-28 after a change by Ernesto, 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