Difference between revisions of "Asynchronous Calls/ru"
(Deleted English categories) |
|||
(4 intermediate revisions by one other user not shown) | |||
Line 5: | Line 5: | ||
При обработке какого-либо события вам иногда нужно что-то сделать, но вы не можете сделать это сразу. Например, вам нужно освободить объект, но на него ссылаются или будут ссылаться позже где-то в родителе (или в его родителе и т.д.). Или вам нужно обновить элементы графического интерфейса, к которым обращаются несколько потоков, безопасным способом. | При обработке какого-либо события вам иногда нужно что-то сделать, но вы не можете сделать это сразу. Например, вам нужно освободить объект, но на него ссылаются или будут ссылаться позже где-то в родителе (или в его родителе и т.д.). Или вам нужно обновить элементы графического интерфейса, к которым обращаются несколько потоков, безопасным способом. | ||
− | == | + | == Решение == |
− | + | Вызовите Application.[[doc:lcl/forms/tapplication.queueasynccall.html |QueueAsyncCall]]: | |
[[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 вызывает этот метод. | |
− | == | + | == Примеры == |
− | === | + | === Передача в асинхронную функцию простых данных === |
− | + | Следующая программа демонстрирует использование [[doc:lcl/forms/tapplication.queueasynccall.html |QueueAsyncCall]]. Если вы нажмете на кнопку CallButton, в LogListBox добавится 'Click 1', 'Click 2' и 'Async 1'. Обратите внимание, что асинхронный вызов выполняется только после завершения события CallButtonClick. | |
− | <syntaxhighlight>unit TestQueueAsyncCall; | + | <syntaxhighlight lang=pascal>unit TestQueueAsyncCall; |
{$mode objfpc}{$H+} | {$mode objfpc}{$H+} | ||
Line 73: | Line 73: | ||
end.</syntaxhighlight> | end.</syntaxhighlight> | ||
− | === | + | === Передача в асинхронную функцию записи === |
− | + | Вот некоторый код, извлеченный из модуля MemoChannel MultiLog, который используется для потоковой записи сообщений в журнал для управления Memo. Благодаря QueueAsyncCall() все записи в Memo сериализуются. Здесь нет блокировок и конфликтов, даже если сотни потоков вызывают метод Write(). | |
− | <syntaxhighlight> | + | <syntaxhighlight lang=pascal> |
... | ... | ||
type | type | ||
− | TLogMsgData = record // | + | TLogMsgData = record // запись может содержать гораздо больше, чем простая строка :-) |
Text: string; | Text: string; | ||
end; | end; | ||
Line 91: | Line 91: | ||
New(LogMsgToSend); | New(LogMsgToSend); | ||
LogMsgToSend^.Text:= AMsg; | LogMsgToSend^.Text:= AMsg; | ||
− | Application.QueueAsyncCall(@WriteAsyncQueue, PtrInt(LogMsgToSend)); // | + | Application.QueueAsyncCall(@WriteAsyncQueue, PtrInt(LogMsgToSend)); // помещаем содержимое msg в очередь, которая будет обработана в основном потоке сразу после обработки всех других сообщений |
end; | end; | ||
... | ... | ||
procedure TMemoChannel.WriteAsyncQueue(Data: PtrInt); | procedure TMemoChannel.WriteAsyncQueue(Data: PtrInt); | ||
− | var // | + | var // вызывается в основном потоке после обработки всех других сообщений, чтобы обеспечить потокобезопасный доступ к TMemo |
ReceivedLogMsg: TLogMsgData; | ReceivedLogMsg: TLogMsgData; | ||
begin | begin | ||
Line 103: | Line 103: | ||
begin | begin | ||
... | ... | ||
− | FMemo.Append(ReceivedLogMsg.Text) // <<< | + | FMemo.Append(ReceivedLogMsg.Text) // <<< полностью потокобезопасен |
end; | end; | ||
finally | finally | ||
Line 111: | Line 111: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
− | == | + | ==См.также== |
− | * [http://lazarus-dev.blogspot.com/2008/01/new-0926-features-part-1-sendmessage.html | + | * [http://lazarus-dev.blogspot.com/2008/01/new-0926-features-part-1-sendmessage.html Новые фичи 0.9.26. Часть 1. SendMessage и PostMessage] |
* [[Main Loop Hooks]] | * [[Main Loop Hooks]] | ||
* [[Multithreaded Application Tutorial]] | * [[Multithreaded Application Tutorial]] | ||
− | |||
− | |||
− |
Latest revision as of 17:39, 27 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 вызывает этот метод.
Примеры
Передача в асинхронную функцию простых данных
Следующая программа демонстрирует использование QueueAsyncCall. Если вы нажмете на кнопку CallButton, в LogListBox добавится 'Click 1', 'Click 2' и 'Async 1'. Обратите внимание, что асинхронный вызов выполняется только после завершения события CallButtonClick.
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.
Передача в асинхронную функцию записи
Вот некоторый код, извлеченный из модуля MemoChannel MultiLog, который используется для потоковой записи сообщений в журнал для управления Memo. Благодаря QueueAsyncCall() все записи в Memo сериализуются. Здесь нет блокировок и конфликтов, даже если сотни потоков вызывают метод Write().
...
type
TLogMsgData = record // запись может содержать гораздо больше, чем простая строка :-)
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)); // помещаем содержимое msg в очередь, которая будет обработана в основном потоке сразу после обработки всех других сообщений
end;
...
procedure TMemoChannel.WriteAsyncQueue(Data: PtrInt);
var // вызывается в основном потоке после обработки всех других сообщений, чтобы обеспечить потокобезопасный доступ к TMemo
ReceivedLogMsg: TLogMsgData;
begin
ReceivedLogMsg := PLogMsgData(Data)^;
try
if (FMemo <> nil) and (not Application.Terminated) then
begin
...
FMemo.Append(ReceivedLogMsg.Text) // <<< полностью потокобезопасен
end;
finally
Dispose(PLogMsgData(Data));
end;
end;