ScottGu имеет набор приемов в http://weblogs.asp.net/scottgu/archive/2006/04/03/441787.aspx
Вы также можете захотеть выполнить свою процедуру в потоке. Затем используйте событие OnTerminate, чтобы получить результат. Да, в наши дни .NET и C # мы в некотором роде избалованы простой и удобной формой асинхронного выполнения методов, но именно так это работает в Delphi.
Если вы спрашиваете, есть ли в VCL что-то вроде BeginInvoke в .NET из коробки, то ответ отрицательный. Однако вы можете получить нечто очень похожее в виде небольшого модуля, который вы подключаете к своей программе, библиотеки AsyncCalls Андреаса Хаусладена. Это не компонент, поэтому я полагаю, что он подходит. Он также поддерживает Delphi, начиная с версии 5. Очень рекомендуется.
Изменить:
Я добавлю пример, так как вы не запустили его. Если вы получаете блокировку в вызывающем коде, ваша проблема заключается в том, что не сохраняется ссылка на указатель интерфейса 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;
Отключите таймер и дайте ему очень короткий интервал
, скажем, 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 ()
.
Приведенный выше код не является абсолютным минимумом,