Sign in Join
Wiki copytemporary

Asynchronous Calls/ru: what changed

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

OldNewLine
11## Постановка задачи
22
33При обработке какого-либо события вам иногда нужно что-то сделать, но вы не можете сделать это сразу. Например, вам нужно освободить объект, но на него ссылаются или будут ссылаться позже где-то в родителе (или в его родителе и т.д.). Или вам нужно обновить элементы графического интерфейса, к которым обращаются несколько потоков, безопасным способом.
44
55## Решение
66
77Вызовите 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;
11+```
1012
11-`procedure `[[doc:lcl/forms/tapplication.queueasynccall.html|QueueAsyncCall]]`(AMethod: TDataEvent; Data: PtrInt);`\
13+``` pascal
14+procedure QueueAsyncCall(AMethod: TDataEvent; Data: PtrInt);
15+```
1216
1317Это «поставит» данный метод с заданным параметром для выполнения в очередь в главном цикле событий, когда все остальные события будут обработаны. В приведенном выше примере ссылка на объект, который вы хотели освободить, исчезла, поскольку тогдашний родительский объект завершил выполнение, и объект, который вы хотели освободить, можно безопасно освободить.
1418
1519Обратите внимание, что это более обобщенная версия [[doc: lcl / forms / tapplication.releasecomponent.html|ReleaseComponent]], и ReleaseComponent вызывает этот метод.
1620
1721## Примеры
1822
1923### Передача в асинхронную функцию простых данных
2024
2125Следующая программа демонстрирует использование [[doc:lcl/forms/tapplication.queueasynccall.html|QueueAsyncCall]]. Если вы нажмете на кнопку CallButton, в LogListBox добавится 'Click 1', 'Click 2' и 'Async 1'. Обратите внимание, что асинхронный вызов выполняется только после завершения события CallButtonClick.
2226
2327``` pascal
2428unit TestQueueAsyncCall;
2529
2630{$mode objfpc}{$H+}
2731
2832interface
2933
3034uses
3135 Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs, Buttons,
3236 StdCtrls;
3337
3438type
3539
3640 { TQueueAsyncCallForm }
3741
3842 TQueueAsyncCallForm = class(TForm)
3943 CallButton: TButton;
4044 LogListBox: TListBox;
4145 procedure CallButtonClick(Sender: TObject);
4246 private
4347 { private declarations }
4448 FCounter: PtrInt;
4549 procedure Async(Data: PtrInt);
4650 public
4751 { public declarations }
4852 end;
4953
5054var
5155 QueueAsyncCallForm: TQueueAsyncCallForm;
5256
5357implementation
5458
5559{$R *.lfm}
5660
5761{ TQueueAsyncCallForm }
5862
5963procedure TQueueAsyncCallForm.CallButtonClick(Sender: TObject);
6064begin
6165 LogListBox.Items.Add('Click 1');
6266 FCounter := FCounter+1;
6367 Application.QueueAsyncCall(@Async,FCounter);
6468 LogListBox.Items.Add('Click 2');
6569end;
6670
6771procedure TQueueAsyncCallForm.Async(Data: PtrInt);
6872begin
6973 LogListBox.Items.Add('Async '+ IntToStr(Data));
7074end;
7175
7276end.
7377```
7478
7579### Передача в асинхронную функцию записи
7680
7781Вот некоторый код, извлеченный из модуля MemoChannel MultiLog, который используется для потоковой записи сообщений в журнал для управления Memo. Благодаря QueueAsyncCall() все записи в Memo сериализуются. Здесь нет блокировок и конфликтов, даже если сотни потоков вызывают метод Write().
7882
7983``` pascal
8084...
8185type
8286 TLogMsgData = record // запись может содержать гораздо больше, чем простая строка :-)
8387 Text: string;
8488 end;
8589 PLogMsgData = ^TLogMsgData;
8690...
8791procedure TMemoChannel.Write(const AMsg: string);
8892var
8993 LogMsgToSend: PLogMsgData;
9094begin
9195 New(LogMsgToSend);
9296 LogMsgToSend^.Text:= AMsg;
9397 Application.QueueAsyncCall(@WriteAsyncQueue, PtrInt(LogMsgToSend)); // помещаем содержимое msg в очередь, которая будет обработана в основном потоке сразу после обработки всех других сообщений
9498end;
9599...
96100procedure TMemoChannel.WriteAsyncQueue(Data: PtrInt);
97101var // вызывается в основном потоке после обработки всех других сообщений, чтобы обеспечить потокобезопасный доступ к TMemo
98102 ReceivedLogMsg: TLogMsgData;
99103begin
100104 ReceivedLogMsg := PLogMsgData(Data)^;
101105 try
102106 if (FMemo <> nil) and (not Application.Terminated) then
103107 begin
104108 ...
105109 FMemo.Append(ReceivedLogMsg.Text) // <<< полностью потокобезопасен
106110 end;
107111 finally
108112 Dispose(PLogMsgData(Data));
109113 end;
110114end;
111115```
112116
113117## См.также
114118
115119- [Новые фичи 0.9.26. Часть 1. SendMessage и PostMessage](http://lazarus-dev.blogspot.com/2008/01/new-0926-features-part-1-sendmessage.html)
116120- [[Main Loop Hooks]]
117121- [[Multithreaded Application Tutorial]]
118122
119123---
120124*From the Free Pascal wiki, page "Asynchronous Calls/ru", as it stood on 2019-09-27 after a change by FTurtle, 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