使用 Omni 线程库在 Delphi 中异步获取函数结果
Get a function result asynchronously in Delphi using Omni Thread Library
我正在尝试从另一个 unit/class 调用一个函数,这将花费一些时间来执行任务并且 return 一个字符串值。我在 Delphi 中找不到类似于 C# async-await 之类简单方法的好参考。使用 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:
这是我试过的。但我仍然无法找出完成工作的正确方法。问题是如何return这个值。
.....
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
我根据提供的答案进行了修改。
主窗体代码:
单位 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.
是的,代码按预期运行。我只是想知道这是否是做我真正想做的事情的最佳方式。
如果您希望在后台执行某些操作但仍需要在相同的执行路径中执行结果,您通常会使用 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 单元中,只需从您的表单调用上面的方法,传递一个事件,该事件将在完成时调用。
我正在尝试从另一个 unit/class 调用一个函数,这将花费一些时间来执行任务并且 return 一个字符串值。我在 Delphi 中找不到类似于 C# async-await 之类简单方法的好参考。使用 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: 这是我试过的。但我仍然无法找出完成工作的正确方法。问题是如何return这个值。
.....
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
我根据提供的答案进行了修改。
主窗体代码: 单位 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.
是的,代码按预期运行。我只是想知道这是否是做我真正想做的事情的最佳方式。
如果您希望在后台执行某些操作但仍需要在相同的执行路径中执行结果,您通常会使用 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 单元中,只需从您的表单调用上面的方法,传递一个事件,该事件将在完成时调用。