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

时间:2015-09-05 17:57:32

标签: multithreading delphi asynchronous delphi-xe omnithreadlibrary

我正在尝试从另一个单元/类调用一个函数,这需要一些时间来执行任务并返回一个字符串值。我找不到一个类似于C#async的好引用 - 就像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。

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

  .....
    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会缩小用户界面。

EDIT2

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

MainForm代码:     单位Unit1;

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.

其他单位代码:     单位Unit2;

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.

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

1 个答案:

答案 0 :(得分:5)

如果您希望在后台执行某些操作但仍需要在同一执行路径中生成结果,则通常会使用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单元中,只需从表单中调用上面的方法,然后传入将在完成时调用的事件。