Sign in Join
Wiki copytemporary

Asynchronous Calls: what changed

Revision 1 to 2. 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
99[[doc:lcl/forms/tdataevent.html|TDataEvent]]` = procedure (Data: PtrInt) of object;`\
10-\
10+
1111`procedure `[[doc:lcl/forms/tapplication.queueasynccall.html|QueueAsyncCall]]`(AMethod: TDataEvent; Data: PtrInt);`\
1212
1313This 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.
1414
1515Note that this is a more generic version of [[doc:lcl/forms/tapplication.releasecomponent.html|ReleaseComponent]], and ReleaseComponent calls this method.
1616
1717## Examples
1818
1919### Simple data passed to async function
2020
2121The 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.
2222
2323``` pascal
2424unit TestQueueAsyncCall;
2525
2626{$mode objfpc}{$H+}
2727
2828interface
2929
3030uses
3131 Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs, Buttons,
3232 StdCtrls;
3333
3434type
3535
3636 { TQueueAsyncCallForm }
3737
3838 TQueueAsyncCallForm = class(TForm)
3939 CallButton: TButton;
4040 LogListBox: TListBox;
4141 procedure CallButtonClick(Sender: TObject);
4242 private
4343 { private declarations }
4444 FCounter: PtrInt;
4545 procedure Async(Data: PtrInt);
4646 public
4747 { public declarations }
4848 end;
4949
5050var
5151 QueueAsyncCallForm: TQueueAsyncCallForm;
5252
5353implementation
5454
5555{$R *.lfm}
5656
5757{ TQueueAsyncCallForm }
5858
5959procedure TQueueAsyncCallForm.CallButtonClick(Sender: TObject);
6060begin
6161 LogListBox.Items.Add('Click 1');
6262 FCounter := FCounter+1;
6363 Application.QueueAsyncCall(@Async,FCounter);
6464 LogListBox.Items.Add('Click 2');
6565end;
6666
6767procedure TQueueAsyncCallForm.Async(Data: PtrInt);
6868begin
6969 LogListBox.Items.Add('Async '+ IntToStr(Data));
7070end;
7171
7272end.
7373```
7474
7575### Record passed to async function
7676
7777Here 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.
7878
7979```delphi
8080...
8181type
8282 TLogMsgData = record // record can hold much more then a simple string :-)
8383 Text: string;
8484 end;
8585 PLogMsgData = ^TLogMsgData;
8686...
8787procedure TMemoChannel.Write(const AMsg: string);
8888var
8989 LogMsgToSend: PLogMsgData;
9090begin
9191 New(LogMsgToSend);
9292 LogMsgToSend^.Text:= AMsg;
9393 Application.QueueAsyncCall(@WriteAsyncQueue, PtrInt(LogMsgToSend)); // put log msg into queue that will be processed from the main thread after all other messages
9494end;
9595...
9696procedure TMemoChannel.WriteAsyncQueue(Data: PtrInt);
9797var // called from main thread after all other messages have been processed to allow thread safe TMemo access
9898 ReceivedLogMsg: TLogMsgData;
9999begin
100100 ReceivedLogMsg := PLogMsgData(Data)^;
101101 try
102102 if (FMemo <> nil) and (not Application.Terminated) then
103103 begin
104104 ...
105105 FMemo.Append(ReceivedLogMsg.Text) // <<< fully thread safe
106106 end;
107107 finally
108108 Dispose(PLogMsgData(Data));
109109 end;
110110end;
111111```
112112
113113## See also
114114
115115- [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)
116116- [New 0.9.26 features. Part 1. SendMessage and PostMessage](http://lazarus-dev.blogspot.com/2008/01/new-0926-features-part-1-sendmessage.html)
117117- [[Main Loop Hooks]]
118118- [[Multithreaded Application Tutorial]]
119119
120120---
121121*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