Sign in Join
Wiki copytemporary

Asynchronous Calls: what changed

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

OldNewLine
11## Problem statement
22
33When handling some event, you need to do something, but you can't do it right away. For example, you need to free an object, but it is or will be referenced somewhere in the parent (or its parent etc.) later on. Or you need to update GUI elements accessed by several threads in a thread safe manner.
44
55## Solution
66
77Call Application.[[doc:lcl/forms/tapplication.queueasynccall.html|QueueAsyncCall]]:
88
9-[[doc:lcl/forms/tdataevent.html|TDataEvent]]` = procedure (Data: PtrInt) of object;`\
9+``` pascal
10+TDataEvent = procedure (Data: PtrInt) of object;
1011
11-`procedure `[[doc:lcl/forms/tapplication.queueasynccall.html|QueueAsyncCall]]`(AMethod: TDataEvent; Data: PtrInt);`\
12+procedure QueueAsyncCall(AMethod: TDataEvent; Data: PtrInt);
13+```
1214
1315This will "queue" the given method with the given parameter for execution in the main event loop, when all other events have been processed. In the example above, the reference to the object you wanted to free has gone, since the then-parent has finished execution, and the object you wanted to free can be freed safely.
1416
1517Note that this is a more generic version of [[doc:lcl/forms/tapplication.releasecomponent.html|ReleaseComponent]], and ReleaseComponent calls this method.
1618
1719## Examples
1820
1921### Simple data passed to async function
2022
2123The following program shows the use of [[doc:lcl/forms/tapplication.queueasynccall.html|QueueAsyncCall]]. If you press on the CallButton, 'Click 1', 'Click 2' and 'Async 1' is added to the LogListBox. Note that Async call is only executed after the CallButtonClick event has finished.
2224
2325``` pascal
2426unit TestQueueAsyncCall;
2527
2628{$mode objfpc}{$H+}
2729
2830interface
2931
3032uses
3133 Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs, Buttons,
3234 StdCtrls;
3335
3436type
3537
3638 { TQueueAsyncCallForm }
3739
3840 TQueueAsyncCallForm = class(TForm)
3941 CallButton: TButton;
4042 LogListBox: TListBox;
4143 procedure CallButtonClick(Sender: TObject);
4244 private
4345 { private declarations }
4446 FCounter: PtrInt;
4547 procedure Async(Data: PtrInt);
4648 public
4749 { public declarations }
4850 end;
4951
5052var
5153 QueueAsyncCallForm: TQueueAsyncCallForm;
5254
5355implementation
5456
5557{$R *.lfm}
5658
5759{ TQueueAsyncCallForm }
5860
5961procedure TQueueAsyncCallForm.CallButtonClick(Sender: TObject);
6062begin
6163 LogListBox.Items.Add('Click 1');
6264 FCounter := FCounter+1;
6365 Application.QueueAsyncCall(@Async,FCounter);
6466 LogListBox.Items.Add('Click 2');
6567end;
6668
6769procedure TQueueAsyncCallForm.Async(Data: PtrInt);
6870begin
6971 LogListBox.Items.Add('Async '+ IntToStr(Data));
7072end;
7173
7274end.
7375```
7476
7577### Record passed to async function
7678
7779Here is some code extracted from MultiLog's MemoChannel unit used for thread safe logging messages to Memo control. Thanks to QueueAsyncCall() all writings to Memo are serialized. There are no locks and no conflicts, even with hundreds of threads calling Write() method.
7880
7981```delphi
8082...
8183type
8284 TLogMsgData = record // record can hold much more then a simple string :-)
8385 Text: string;
8486 end;
8587 PLogMsgData = ^TLogMsgData;
8688...
8789procedure TMemoChannel.Write(const AMsg: string);
8890var
8991 LogMsgToSend: PLogMsgData;
9092begin
9193 New(LogMsgToSend);
9294 LogMsgToSend^.Text:= AMsg;
9395 Application.QueueAsyncCall(@WriteAsyncQueue, PtrInt(LogMsgToSend)); // put log msg into queue that will be processed from the main thread after all other messages
9496end;
9597...
9698procedure TMemoChannel.WriteAsyncQueue(Data: PtrInt);
9799var // called from main thread after all other messages have been processed to allow thread safe TMemo access
98100 ReceivedLogMsg: TLogMsgData;
99101begin
100102 ReceivedLogMsg := PLogMsgData(Data)^;
101103 try
102104 if (FMemo <> nil) and (not Application.Terminated) then
103105 begin
104106 ...
105107 FMemo.Append(ReceivedLogMsg.Text) // <<< fully thread safe
106108 end;
107109 finally
108110 Dispose(PLogMsgData(Data));
109111 end;
110112end;
111113```
112114
113115## See also
114116
115117- [TThread.Queue, which provides the same functionality in a Delphi compatible way and with anonymous method support in FPC 3.3.x and later](https://www.freepascal.org/docs-html/rtl/classes/tthread.queue.html)
116118- [New 0.9.26 features. Part 1. SendMessage and PostMessage](http://lazarus-dev.blogspot.com/2008/01/new-0926-features-part-1-sendmessage.html)
117119- [[Main Loop Hooks]]
118120- [[Multithreaded Application Tutorial]]
119121
120122---
121123*From the Free Pascal wiki, page "Asynchronous Calls", as it stood on 2023-09-07 after a change by Marcov, 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