Как вызвать функцию / процедуру асинхронно в Delphi (без компонентов) - PullRequest
9 голосов
/ 02 декабря 2009

Я пытаюсь запустить функцию или процедуру в Delphi асинхронно, но без использования компонента, есть ли способ сделать это с помощью основных функций Delphi?

Ответы [ 2 ]

17 голосов
/ 02 декабря 2009

Если вы спрашиваете, есть ли у VCL что-то вроде BeginInvoke в .NET из коробки, тогда ответ будет отрицательным. Однако вы можете получить нечто очень похожее в виде небольшого блока, который вы связываете с вашей программой, библиотеки AsyncCalls от Andreas Hausladen. Это не компонент, так что, я думаю, это подходит. Он также поддерживает Delphi начиная с версии 5. Очень рекомендуется.

Edit:

Я добавлю пример, так как вы не запустили его. Если вы получаете блокировку в своем вызывающем коде, то ваша проблема в том, что не сохраняется ссылка на указатель интерфейса IAsyncCall, который вернула функция. Поэтому объект, реализующий интерфейс, будет немедленно уничтожен, когда временная ссылка выйдет из области видимости. Деструктор будет вызываться в контексте потока VCL и будет вызывать WaitForSingleObject() или аналогичную функцию, ожидающую завершения рабочего потока. В результате ваш поток VCL блокируется.

Вы получите правильное поведение, если сохраните ссылку на указатель интерфейса:

type
  TForm1 = class(TForm)
    Button1: TButton;
    Timer1: TTimer;
    procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
    procedure Button1Click(Sender: TObject);
    procedure Timer1Timer(Sender: TObject);
  private
    fAsyncCall: IAsyncCall;
    procedure WaitForIt(ADelay: integer);
  end;

Установите таймер на отключенный и дайте ему очень короткий Interval, скажем, 50 мс. Нажатие кнопки запускает асинхронную операцию:

procedure TForm1.Button1Click(Sender: TObject);
begin
  Button1.Enabled := FALSE;
  fAsyncCall := AsyncCall(WaitForIt, 1000);
end;

procedure TForm1.WaitForIt(ADelay: integer);
begin
  Sleep(ADelay);

  EnterMainThread;
  try
    Randomize;
    Color := RGB(Random(256), Random(256), Random(256));
    Timer1.Enabled := TRUE;
  finally
    LeaveMainThread;
  end;
end;

Пока операция активна, запуск других операций невозможен. По завершении он позволяет таймеру уведомить форму и сбросить ссылку на интерфейс:

procedure TForm1.Timer1Timer(Sender: TObject);
begin
  Timer1.Enabled := FALSE;
  Assert((fAsyncCall <> nil) and fAsyncCall.Finished);
  fAsyncCall := nil;
  Button1.Enabled := TRUE;
end;

procedure TForm1.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin
  CanClose := (fAsyncCall = nil) or fAsyncCall.Finished;
end;

Обратите внимание, как можно получить доступ к форме напрямую из вызываемого метода, используя EnterMainThread() и LeaveMainThread().

Выше код не является абсолютным минимумом, он предназначен только для демонстрации некоторых идей.

5 голосов
/ 02 декабря 2009

Вы также можете выполнить свою процедуру в потоке. Затем используйте событие OnTerminate, чтобы получить результат. Да, в наши дни .NET и C # мы в некотором роде избалованы простой и удобной формой асинхронного выполнения методов, но так оно и работает в Delphi.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...