Difference between revisions of "Asynchronous Calls/ru"

From Free Pascal wiki
Jump to navigationJump to search
(Deleted English categories)
 
(2 intermediate revisions by one other user not shown)
Line 17: Line 17:
 
Обратите внимание, что это более обобщенная версия [[doc: lcl / forms / tapplication.releasecomponent.html|ReleaseComponent]], и ReleaseComponent вызывает этот метод.
 
Обратите внимание, что это более обобщенная версия [[doc: lcl / forms / tapplication.releasecomponent.html|ReleaseComponent]], и ReleaseComponent вызывает этот метод.
  
== Examples ==
+
== Примеры ==
  
=== Simple data passed to async function ===
+
=== Передача в асинхронную функцию простых данных  ===
  
The 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.
+
Следующая программа демонстрирует использование [[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>
  
=== 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.
+
Вот некоторый код, извлеченный из модуля MemoChannel MultiLog, который используется для потоковой записи сообщений в журнал для управления Memo. Благодаря QueueAsyncCall() все записи в Memo сериализуются. Здесь нет блокировок и конфликтов, даже если сотни потоков вызывают метод Write().
  
<syntaxhighlight>
+
<syntaxhighlight lang=pascal>
 
...
 
...
 
type
 
type
   TLogMsgData = record // record can hold much more then a simple string :-)
+
   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)); // put log msg into queue that will be processed from the main thread after all other messages
+
   Application.QueueAsyncCall(@WriteAsyncQueue, PtrInt(LogMsgToSend)); // помещаем содержимое msg в очередь, которая будет обработана в основном потоке сразу после обработки всех других сообщений
 
end;
 
end;
 
...
 
...
 
procedure TMemoChannel.WriteAsyncQueue(Data: PtrInt);
 
procedure TMemoChannel.WriteAsyncQueue(Data: PtrInt);
var // called from main thread after all other messages have been processed to allow thread safe TMemo access
+
var // вызывается в основном потоке после обработки всех других сообщений, чтобы обеспечить потокобезопасный доступ к TMemo
 
   ReceivedLogMsg: TLogMsgData;
 
   ReceivedLogMsg: TLogMsgData;
 
begin
 
begin
Line 103: Line 103:
 
     begin
 
     begin
 
       ...
 
       ...
       FMemo.Append(ReceivedLogMsg.Text) // <<< fully thread safe
+
       FMemo.Append(ReceivedLogMsg.Text) // <<< полностью потокобезопасен
 
     end;
 
     end;
 
   finally
 
   finally
Line 111: Line 111:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
==See also==
+
==См.также==
  
* [http://lazarus-dev.blogspot.com/2008/01/new-0926-features-part-1-sendmessage.html New 0.9.26 features. Part 1. SendMessage and PostMessage]
+
* [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]]
 
[[Category:Tutorials]]
 
[[Category:Lazarus]]
 

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;

См.также