如何在Delphi中异步调用函数/过程(没有组件)

时间:2009-12-02 04:06:27

标签: delphi asynchronous callback

我试图在Delphi中异步运行一个函数或过程,但是没有使用组件,有没有办法用delphi核心函数来做呢?

2 个答案:

答案 0 :(得分:17)

如果你问VCL是否有开箱即用的like BeginInvoke in .NET,那么答案是否定的。但是,您可以通过Andreas Hausladen的AsyncCalls库链接到您的程序的小单元形式获得非常相似的内容。它不是一个组件,所以我猜它有资格。它还支持从版本5开始的Delphi。非常推荐。

修改

我会添加一个例子,因为你没有让它运行。如果你的调用代码被阻塞,那么你的问题是没有引用该函数返回的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 ms。按钮单击启动异步操作:

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()直接从被调用的方法访问表单。

以上代码不是绝对最低限度,只是为了展示一些想法。

答案 1 :(得分:5)

您可能还想在线程上执行您的过程。然后使用OnTerminate事件来获取结果。是的,在.NET和C#的这些日子里,我们对异步执行方法的简单方便形式有所不满,但这就是它在Delphi上运行的方式。