Sign in Join
Wiki copytemporary

Asynchronous Calls/ru: what changed

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

Title: Asynchronous Calls/ru

OldNewLine
1+## Постановка задачи
2+
3+При обработке какого-либо события вам иногда нужно что-то сделать, но вы не можете сделать это сразу. Например, вам нужно освободить объект, но на него ссылаются или будут ссылаться позже где-то в родителе (или в его родителе и т.д.). Или вам нужно обновить элементы графического интерфейса, к которым обращаются несколько потоков, безопасным способом.
4+
5+## Решение
6+
7+Вызовите 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+Это «поставит» данный метод с заданным параметром для выполнения в очередь в главном цикле событий, когда все остальные события будут обработаны. В приведенном выше примере ссылка на объект, который вы хотели освободить, исчезла, поскольку тогдашний родительский объект завершил выполнение, и объект, который вы хотели освободить, можно безопасно освободить.
14+
15+Обратите внимание, что это более обобщенная версия [[doc: lcl / forms / tapplication.releasecomponent.html|ReleaseComponent]], и ReleaseComponent вызывает этот метод.
16+
17+## Примеры
18+
19+### Передача в асинхронную функцию простых данных
20+
21+Следующая программа демонстрирует использование [[doc:lcl/forms/tapplication.queueasynccall.html|QueueAsyncCall]]. Если вы нажмете на кнопку CallButton, в LogListBox добавится 'Click 1', 'Click 2' и 'Async 1'. Обратите внимание, что асинхронный вызов выполняется только после завершения события CallButtonClick.
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+### Передача в асинхронную функцию записи
76+
77+Вот некоторый код, извлеченный из модуля MemoChannel MultiLog, который используется для потоковой записи сообщений в журнал для управления Memo. Благодаря QueueAsyncCall() все записи в Memo сериализуются. Здесь нет блокировок и конфликтов, даже если сотни потоков вызывают метод Write().
78+
79+``` pascal
80+...
81+type
82+ TLogMsgData = record // запись может содержать гораздо больше, чем простая строка :-)
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)); // помещаем содержимое msg в очередь, которая будет обработана в основном потоке сразу после обработки всех других сообщений
94+end;
95+...
96+procedure TMemoChannel.WriteAsyncQueue(Data: PtrInt);
97+var // вызывается в основном потоке после обработки всех других сообщений, чтобы обеспечить потокобезопасный доступ к TMemo
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) // <<< полностью потокобезопасен
106+ end;
107+ finally
108+ Dispose(PLogMsgData(Data));
109+ end;
110+end;
111+```
112+
113+## См.также
114+
115+- [Новые фичи 0.9.26. Часть 1. SendMessage и PostMessage](http://lazarus-dev.blogspot.com/2008/01/new-0926-features-part-1-sendmessage.html)
116+- [[Main Loop Hooks]]
117+- [[Multithreaded Application Tutorial]]
118+
119+---
120+*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