Как вызвать функцию/процедуру асинхронно в Delphi (без компонентов)

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

2 ответов


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


если вы спрашиваете, есть ли у 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().

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