multithreading - 使用 Omni Thread Library 在 Delphi 中异步获取函数结果

标签 multithreading delphi asynchronous delphi-xe omnithreadlibrary

我正在尝试从另一个单元/类调用一个函数,这将需要一些时间来执行任务并返回一个字符串值。我找不到类似于 C# async-await 的好的引用,比如 Delphi 中的简单方法。使用 Omni Thread 库对我来说似乎是个好主意。

一个简单的例子对我来说将是一个很好的开始。

示例方法:

procedure TForm1.button1Click(Sender: TObject);
begin
  // notify before starting the task
  memo1.Lines.Add('calling a asynchronous function..');

  // call to the function that takes some time and returns a string value
  memo1.Lines.Add(GetMagicString);

  // notify that the task has been completed
  memo1.Lines.Add('Results fetched successfully.');
end;

这里,函数GetMagicString应该异步处理结果。一旦获得结果,程序才应通知任务已完成。顺便说一句,我正在使用 Delphi-XE。

编辑1: 这是我尝试过的。但我仍然无法找出完成工作的正确方法。问题是如何返回值。

  .....
    private
      ResultValue: IOmniFuture<string>;
    .........
    .....


    function TForm1.FutureGet: string;
    begin
      Sleep(3000);
      Result := 'my sample magic string response ' +  IntToStr(Random(9999));
    end;

    procedure TForm1.FutureGetTerminated;
    begin
      // This code fired when the task is completed
      memo1.Lines.Add(ResultValue.Value);
    end;

    function TForm1.GetMagicString: string;
    begin
      ResultValue := Parallel.Future<string>(FutureGet,
            Parallel.TaskConfig.OnTerminated(FutureGetTerminated));

    end;

在这里,使用 Result := ResultValue.Value 会影响 UI。

编辑2

我根据提供的答案进行了更改。

主窗体代码: 单元单元1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, Unit2;



type
  TForm1 = class(TForm)
    memo1: TMemo;
    button1: TButton;
    procedure button1Click(Sender: TObject);
  private
    FOnStringReceived: TMyEvent;
    procedure StringReceived(const AValue: string);
    property OnStringReceived: TMyEvent read FOnStringReceived write FOnStringReceived;
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}


procedure TForm1.button1Click(Sender: TObject);
var
  MyObject: TMyClass;
begin
  // notify before starting the task
  memo1.Lines.Add('calling a asynchronous function..');

  // call to the function that takes some time and returns a string value
  MyObject := TMyClass.Create;
  OnStringReceived := StringReceived;
  try
    MyObject.GetMagicStringInBackground(OnStringReceived);
  finally
    MyObject.Free;
  end;
end;


procedure TForm1.StringReceived(const AValue: string);
begin
  memo1.Lines.Add(AValue);

   // notify that the task has been completed
  memo1.Lines.Add('Results fetched successfully.');
end;
end.

其他单位代码: 单元单元2;

interface

uses SysUtils, OtlTask, OtlParallel, OtlTaskControl;

type
  TMyEvent = procedure(const aValue: string) of object;

type
  TMyClass = class
  private
    FOnStringReceived: TMyEvent;
    function GetMagicString: string;
  public
    procedure GetMagicStringInBackground(AEvent: TMyEvent);
end;

implementation

{ TMyClass }

function TMyClass.GetMagicString: string;
begin
  Sleep(3000);
  Result := 'my sample magic string response ' +  IntToStr(Random(9999));
end;

procedure TMyClass.GetMagicStringInBackground(AEvent: TMyEvent);
var
  theFunctionResult: string;
begin
  Parallel.Async(
    procedure
    begin
      theFunctionResult := GetMagicString;
    end,

    Parallel.TaskConfig.OnTerminated(
    procedure (const task: IOmniTaskControl)
    begin
      if Assigned(AEvent) then
        AEvent(theFunctionResult);
    end)
  );
end;
end.

是的,代码按预期工作。我只是想知道这是否是做我真正想做的事情的最佳方式。

最佳答案

当您希望在后台执行某些操作但仍需要同一执行路径中的结果时,您通常会使用 future。它基本上可以让您在后台做某事,同时在主线程中做另一件事,然后您可以使用后台线程的结果。

您需要使用的是 TLama 链接到的异步抽象:

在你的情况下,它是:

procedure TForm1.DoSomething;
var
  theFunctionResult: string;
begin
  memo1.Lines.Add('calling a asynchronous function..');
  Parallel.Async(
    procedure
    begin
      // executed in background thread
      theFunctionResult := GetMagicString;
    end,

    procedure
    begin
      // executed in main thread after the async has finished
      memo1.Lines.Add(theFunctionResult);

      // notify that the task has been completed
      memo1.Lines.Add('Results fetched successfully.');
    end
  );
end;

这有点困惑,但你应该明白了。在销毁拥有此代码的表单 (TForm1) 之前,您需要确保异步代码已完成。

如果您想尝试设置一个在代码完成时调用事件的系统,那么您可以执行以下操作:

type
  TMyEvent = procedure(const aValue: string) of object;

procedure GetMagicStringInBackground(AEvent: TMyEvent);
var
  theFunctionResult: string;
begin
  Parallel.Async(
    procedure
    begin
      // executed in background thread
      theFunctionResult := GetMagicString;
    end,

    Parallel.TaskConfig.OnTerminated(
      procedure (const task: IOmniTaskControl)
      begin
        // executed in main thread after the async has finished
        if Assigned(AEvent) then
          AEvent(theFunctionResult );
      end
    )
  );
end;

然后,您可以将线程代码放入 GetMagicString 单元中,然后从表单中调用上面的方法,传递一个事件,该事件将在完成时调用。

关于multithreading - 使用 Omni Thread Library 在 Delphi 中异步获取函数结果,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32416100/

相关文章:

c - 使用 mutex_destroy() 时如何释放内存 : Posix multithreading

java - lambda 中的无限 while 循环内的 Thread.sleep 不需要 'catch (InterruptedException)' - 为什么不呢?

delphi - 将 Indy9 升级到 Indy10

xml - Delphi 2010 中的 C# 样式 XML 注释

windows - 为什么 ReadDirectoryChangesW 会忽略事件?

CPU占用显示100%

java - cachedThreadPool 没有按我的预期工作

node.js - 如何使用 Jest 模拟异步函数延迟时间

python - Tornado .httpclient.HTTPError : HTTP 599: Timeout during request

asynchronous - 异步对调用者滚雪球,不能使构造函数异步