2

我想從另一個單元/類調用一個函數,這將需要一些時間來執行任務並返回一個字符串值。在Delphi中我找不到像C#異步等待類似的簡單方法。使用Omni線程庫對我來說似乎是個好主意。使用Omni線程庫在Delphi中異步獲取函數結果

一個簡單的例子對我來說是一個很好的開始。

樣品的方法:

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。

EDIT2

我所做的更改按照所提供的答案。

MainForm代碼: unit 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

這並不是所有的異步 –

+1

http://www.thedelphigeek.com/2011/04/simple-background-tasks-with.html – TLama

+0

@DavidHeffernan是的,這根本不是異步。其實,這是我想要實現的方法。我編輯了這個問題。請hava熟悉它。 –

回答

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單元和公正調用上面的方法從表單中傳遞一個事件,當它完成時會被調用。

+0

您的意思是我必須在窗體單元中實現線程部分來獲取任務結果嗎?如果沒有使用OTL等待Delphi的結果,那麼我將使用這種機制。 –

+0

@RabiJayasawal這是等待結果。第二個匿名方法將只在螺紋部分完成時執行。螺紋部分可以放在任何地方。爲什麼我把它放在表單元中的唯一原因是因爲它正在訪問表單變量(memo1)。 – Graymatter

+0

正如我前面所述,函數** GetMagicString **存在於另一個單元中。我需要從表單單元調用它。線程代碼將位於函數** GetMagicString **所在的同一單元中。你的代碼很好。我只想知道我是否可以按照要求執行。如果NO是答案,我會遵循你的方法並接受你的答案。 –