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

Seb*_*ian 9 delphi asynchronous callback

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

mgh*_*hie 17

如果你问的是VCL是否具有开箱即用的.NET中的BeginInvoke,那么答案就是否定的.但是,您可以通过链接到程序的小单元形式获得非常相似的东西,即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;
Run Code Online (Sandbox Code Playgroud)

将定时器设置为禁用,让它有一个非常短的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;
Run Code Online (Sandbox Code Playgroud)

当操作处于活动状态时,无法启动其他操作.完成后,它使计时器能够通知表单并重置接口引用:

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;
Run Code Online (Sandbox Code Playgroud)

注意如何使用EnterMainThread()和直接从被调用的方法访问表单LeaveMainThread().

以上代码不是绝对最小值,仅用于展示一些想法.


Lob*_*uno 5

您可能还想在线程上执行您的过程.然后使用OnTerminate事件来获取结果.是的,在.NET和C#的这些日子里,我们被某种简单易用的执行方法所破坏,但这就是它在Delphi上运行的方式.