Difference between revisions of "Asynchronous Calls/ru"
Line 10: | Line 10: | ||
[[doc:lcl/forms/tdataevent.html|TDataEvent]] = procedure (Data: PtrInt) of object; | [[doc:lcl/forms/tdataevent.html|TDataEvent]] = procedure (Data: PtrInt) of object; | ||
− | + | ||
procedure [[doc:lcl/forms/tapplication.queueasynccall.html |QueueAsyncCall]](AMethod: TDataEvent; Data: PtrInt); | procedure [[doc:lcl/forms/tapplication.queueasynccall.html |QueueAsyncCall]](AMethod: TDataEvent; Data: PtrInt); | ||
− | + | Это «поставит» данный метод с заданным параметром для выполнения в очередь в главном цикле событий, когда все остальные события будут обработаны. В приведенном выше примере ссылка на объект, который вы хотели освободить, исчезла, поскольку тогдашний родительский объект завершил выполнение, и объект, который вы хотели освободить, можно безопасно освободить. | |
− | + | Обратите внимание, что это более обобщенная версия [[doc: lcl / forms / tapplication.releasecomponent.html|ReleaseComponent]], и ReleaseComponent вызывает этот метод. | |
== Examples == | == Examples == |
Revision as of 13:15, 26 September 2019
│
English (en) │
français (fr) │
日本語 (ja) │
русский (ru) │
Постановка задачи
При обработке какого-либо события вам иногда нужно что-то сделать, но вы не можете сделать это сразу. Например, вам нужно освободить объект, но на него ссылаются или будут ссылаться позже где-то в родителе (или в его родителе и т.д.). Или вам нужно обновить элементы графического интерфейса, к которым обращаются несколько потоков, безопасным способом.
Решение
Вызовите Application.QueueAsyncCall:
TDataEvent = procedure (Data: PtrInt) of object;
procedure QueueAsyncCall(AMethod: TDataEvent; Data: PtrInt);
Это «поставит» данный метод с заданным параметром для выполнения в очередь в главном цикле событий, когда все остальные события будут обработаны. В приведенном выше примере ссылка на объект, который вы хотели освободить, исчезла, поскольку тогдашний родительский объект завершил выполнение, и объект, который вы хотели освободить, можно безопасно освободить.
Обратите внимание, что это более обобщенная версия ReleaseComponent, и ReleaseComponent вызывает этот метод.
Examples
Simple data passed to async function
The following program shows the use of 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.
unit TestQueueAsyncCall;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs, Buttons,
StdCtrls;
type
{ TQueueAsyncCallForm }
TQueueAsyncCallForm = class(TForm)
CallButton: TButton;
LogListBox: TListBox;
procedure CallButtonClick(Sender: TObject);
private
{ private declarations }
FCounter: PtrInt;
procedure Async(Data: PtrInt);
public
{ public declarations }
end;
var
QueueAsyncCallForm: TQueueAsyncCallForm;
implementation
{$R *.lfm}
{ TQueueAsyncCallForm }
procedure TQueueAsyncCallForm.CallButtonClick(Sender: TObject);
begin
LogListBox.Items.Add('Click 1');
FCounter := FCounter+1;
Application.QueueAsyncCall(@Async,FCounter);
LogListBox.Items.Add('Click 2');
end;
procedure TQueueAsyncCallForm.Async(Data: PtrInt);
begin
LogListBox.Items.Add('Async '+ IntToStr(Data));
end;
end.
Record passed to async function
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.
...
type
TLogMsgData = record // record can hold much more then a simple string :-)
Text: string;
end;
PLogMsgData = ^TLogMsgData;
...
procedure TMemoChannel.Write(const AMsg: string);
var
LogMsgToSend: PLogMsgData;
begin
New(LogMsgToSend);
LogMsgToSend^.Text:= AMsg;
Application.QueueAsyncCall(@WriteAsyncQueue, PtrInt(LogMsgToSend)); // put log msg into queue that will be processed from the main thread after all other messages
end;
...
procedure TMemoChannel.WriteAsyncQueue(Data: PtrInt);
var // called from main thread after all other messages have been processed to allow thread safe TMemo access
ReceivedLogMsg: TLogMsgData;
begin
ReceivedLogMsg := PLogMsgData(Data)^;
try
if (FMemo <> nil) and (not Application.Terminated) then
begin
...
FMemo.Append(ReceivedLogMsg.Text) // <<< fully thread safe
end;
finally
Dispose(PLogMsgData(Data));
end;
end;