Sign in Join
Wiki copytemporary

Asynchronous Calls: what changed

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

Title: Asynchronous Calls

OldNewLine
1+## Problem statement
2+
3+When 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.
4+
5+## Solution
6+
7+Call Application.[[doc:lcl/forms/tapplication.queueasynccall.html|QueueAsyncCall]]:
8+
9+[[doc:lcl/forms/tdataevent.html|TDataEvent]]` = procedure (Data: PtrInt) of object;`\
10+\
11+`procedure `[[doc:lcl/forms/tapplication.queueasynccall.html|QueueAsyncCall]]`(AMethod: TDataEvent; Data: PtrInt);`\
12+
13+This 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.
14+
15+Note that this is a more generic version of [[doc:lcl/forms/tapplication.releasecomponent.html|ReleaseComponent]], and ReleaseComponent calls this method.
16+
17+## Examples
18+
19+### Simple data passed to async function
20+
21+The 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.
22+
23+``` pascal
24+unit TestQueueAsyncCall;
25+
26+{$mode objfpc}{$H+}
27+
28+interface
29+
30+uses
31+ Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs, Buttons,
32+ StdCtrls;
33+
34+type
35+
36+ { TQueueAsyncCallForm }
37+
38+ TQueueAsyncCallForm = class(TForm)
39+ CallButton: TButton;
40+ LogListBox: TListBox;
41+ procedure CallButtonClick(Sender: TObject);
42+ private
43+ { private declarations }
44+ FCounter: PtrInt;
45+ procedure Async(Data: PtrInt);
46+ public
47+ { public declarations }
48+ end;
49+
50+var
51+ QueueAsyncCallForm: TQueueAsyncCallForm;
52+
53+implementation
54+
55+{$R *.lfm}
56+
57+{ TQueueAsyncCallForm }
58+
59+procedure TQueueAsyncCallForm.CallButtonClick(Sender: TObject);
60+begin
61+ LogListBox.Items.Add('Click 1');
62+ FCounter := FCounter+1;
63+ Application.QueueAsyncCall(@Async,FCounter);
64+ LogListBox.Items.Add('Click 2');
65+end;
66+
67+procedure TQueueAsyncCallForm.Async(Data: PtrInt);
68+begin
69+ LogListBox.Items.Add('Async '+ IntToStr(Data));
70+end;
71+
72+end.
73+```
74+
75+### Record passed to async function
76+
77+Here 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.
78+
79+```delphi
80+...
81+type
82+ TLogMsgData = record // record can hold much more then a simple string :-)
83+ Text: string;
84+ end;
85+ PLogMsgData = ^TLogMsgData;
86+...
87+procedure TMemoChannel.Write(const AMsg: string);
88+var
89+ LogMsgToSend: PLogMsgData;
90+begin
91+ New(LogMsgToSend);
92+ LogMsgToSend^.Text:= AMsg;
93+ Application.QueueAsyncCall(@WriteAsyncQueue, PtrInt(LogMsgToSend)); // put log msg into queue that will be processed from the main thread after all other messages
94+end;
95+...
96+procedure TMemoChannel.WriteAsyncQueue(Data: PtrInt);
97+var // called from main thread after all other messages have been processed to allow thread safe TMemo access
98+ ReceivedLogMsg: TLogMsgData;
99+begin
100+ ReceivedLogMsg := PLogMsgData(Data)^;
101+ try
102+ if (FMemo <> nil) and (not Application.Terminated) then
103+ begin
104+ ...
105+ FMemo.Append(ReceivedLogMsg.Text) // <<< fully thread safe
106+ end;
107+ finally
108+ Dispose(PLogMsgData(Data));
109+ end;
110+end;
111+```
112+
113+## See also
114+
115+- [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)
116+- [New 0.9.26 features. Part 1. SendMessage and PostMessage](http://lazarus-dev.blogspot.com/2008/01/new-0926-features-part-1-sendmessage.html)
117+- [[Main Loop Hooks]]
118+- [[Multithreaded Application Tutorial]]
119+
120+---
121+*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