Delphi 知识 并行编程 Tasks
创始人
2025-05-30 09:05:24
0

自Delphi XE7以来,支持了并行编程库 Parallel Programming Library (PPL)。那么什么是PPL呢?PPL是Delphi RTL的一部分,它为多线程(或并行)编程提供了极大便利。

PPL适用于Delphi所支持的所有平台,并提供了一些先进的功能,如运行任务、连接任务、等待任务执行等。PPL不同 Thread,因为PPL 支持线程池,而且能够自动管理基于CPU上的负载),不用开发者关心和创建这些线程。

Delphi的PPL的使用相当简单,不需要对你的应用程序进行大的改动。在程序中加入System.Threading单元就可以这个库。

问题是我们如何以及何时可以使用PPL?嗯,在通常的情况下,你考虑一个任务,这并不意味着你将不再创建线程,但是,在许多情况下,你将使用排序的任务而非普通的线程。

使用简单的PPL,典型的代码示例如下:

procedure TMainForm.btnITaskClick(Sender: TObject);
varLTask: ITask;
beginLTask := TTask.Run(procedurevarLResult: Integer;beginSleep(1000); //并行做一些事情...LResult := Random(100); //获取执行结果 "result"//Queue 在主进程中执行(主进程执行方法一)TThread.Queue(nil,procedurebeginTaskEnd(LResult); //TaskEnd 被主 UI 调用end);//或者 Synchronize 在主进程中执行(主进程执行方法二)TThread.Synchronize(TThread.Current,procedurebeginShowMessage ('Hello');end);end);
end;

上述函数使用起来非常简单,但是当有很多任务运行时,同时还需要处理异常时,事情就不是那么简单了。

此时,我们可以扩充下ITask接口,例如扩充为:Async.Run。对Async.Run方法的完整调用是由三个匿名的方法组成。

一个后台任务:这是一个返回某种数据的函数。它运行在在一个使用PPL任务的后台线程中运行。

一个成功回调:这是一个过程,用于获得后台任务的结果。它在主UI线程中运行。

一个错误回调:这是一个过程,用于获取由后台任务引发的异常,如果有的话。它在主UI线程中运行。

整体结构看起来如下:

Async.Run(
function: String
begin
//这是 "后台 "匿名方法。运行在后台线程中运行,其结果被传递到
//到 "成功 "回调过程中。
//在当前架构情况下,结果是一个字符串。
end,
procedure(const Value: String)
begin
//这是 "成功 "回调。在UI线程中运行并
//获取 "后台 "匿名方法的结果。
end,
procedure(const Ex: Exception)
begin
//这是 "错误 "回调。
//在UI线程中运行,只有在 "后台 "匿名方法产生异常时才会被调用。
//"后台 "匿名方法引发异常时才会被调用。
end)

下面是一个完整的 AsyncTask.pas 单元:

unit AsyncTask;
interface
usesSystem.SysUtils,System.Threading; //The PPL unit
type//"后台 "任务TAsyncBackgroundTask = reference to function: T;//"成功 "回调过程TAsyncSuccessCallback = reference to procedure(const TaskResult: T);//"错误 "回调过程TAsyncErrorCallback = reference to procedure(const E: Exception);//如果用户没有提供默认的 "错误 "回调,则下面为默认的 "错误 "回调。TAsyncDefaultErrorCallback = reference to procedure(const E: Exception;const ExptAddress: Pointer);//主类Async = class sealed   //一个sealed (封闭)的类不能通过继承来扩展。publicclass function Run(Task: TAsyncBackgroundTask; Success: TAsyncSuccessCallback;Error  : TAsyncErrorCallback = nil): ITask;end;
var//默认的 "错误 "回调。它是一个公共变量,以便于可以覆盖默认行为DefaultTaskErrorHandler: TAsyncDefaultErrorCallback = nil;implementation
usesSystem.Classes;class function Async.Run(Task: TAsyncBackgroundTask; Success: TAsyncSuccessCallback;Error: TAsyncErrorCallback): ITask;
varLRes: T;
begin//“后台 ”任务从这里开始Result := TTask.Run(procedurevarEx: Pointer;ExceptionAddress: Pointer;beginEx := nil;tryLRes := Task(); //运行实际的任务if Assigned(Success) thenbegin//调用成功回调,传递结果。TThread.Queue(nil,procedurebeginSuccess(LRes);end);end;except//让我们扩充异常对象。Ex := AcquireExceptionObject;ExceptionAddress := ExceptAddr;//在主线程上排队,调用错误回调。TThread.Queue(nil,procedurevarLCurrException: Exception;beginLCurrException := Exception(Ex);tryif Assigned(Error) thenError(LCurrException) //调用 "错误 "回调elseDefaultTaskErrorHandler(LCurrException, ExceptionAddress);finally//释放异常对象。它是必要的,因为上面 "扩充 "了异常对象的自然寿命//异常对象的自然寿命超过了 except 块FreeAndNil(LCurrException);end;end);end; //exceptend); //task.run
end;initialization//这是默认的错误回调。
DefaultTaskErrorHandler :=procedure(const E: Exception; const ExceptionAddress: Pointer)beginShowException(E, ExceptionAddress);end;
end.

上面我们知道Async.Run是如何实现的,现在让我们看看如何使用它。打开一个主Form,放置一个

btnSimple按钮。

procedure TMainForm.btnSimpleClick(Sender: TObject);
begin
Async.Run(function: Integer //在后台长时间运行beginSleep(2000);Result := Random(100);end,procedure(const Value: Integer) //在用户界面中显示结果begin//将结果写在一个备忘录中Log('RESULT: ' + Value.ToString);end);
end;

在上面这种情况下,长的和假的操作是在匿名方法中执行的。作为一个返回整数的函数。当该函数结束时,其返回值被传递给另一个匿名方法,它是一个过程,并在UI线程中运行。

线程中运行,这样它就可以与用户进行交互。如果你运行该程序并点击这个按钮,你可以验证UI在长操作的时候没有被冻结。(实际上是一个Sleep(2000)的调用)运行时,UI没有被冻结。

放置第二个按钮为btnWithException,显示了如何处理可能在后台线程中引发的异常。

procedure TMainForm.btnWithExceptionClick(Sender: TObject);
begin
Async.Run(function: Stringbeginraise Exception.Create('This is an error message');end,procedure(const Value: String)begin// never calledend,procedure(const Ex: Exception)beginLog('Exception: ' + sLineBreak + Ex.Message);end);
end;

相当简单,是不是?如果在后台出了问题,相关的异常对象被传递给错误回调。请注意,这个错误块

不是一个标准的Delphi异常块,它只是一个匿名方法,实际可以获得一个异常对象。

相关内容

热门资讯

喜欢穿一身黑的男生性格(喜欢穿... 今天百科达人给各位分享喜欢穿一身黑的男生性格的知识,其中也会对喜欢穿一身黑衣服的男人人好相处吗进行解...
发春是什么意思(思春和发春是什... 本篇文章极速百科给大家谈谈发春是什么意思,以及思春和发春是什么意思对应的知识点,希望对各位有所帮助,...
网络用语zl是什么意思(zl是... 今天给各位分享网络用语zl是什么意思的知识,其中也会对zl是啥意思是什么网络用语进行解释,如果能碰巧...
为什么酷狗音乐自己唱的歌不能下... 本篇文章极速百科小编给大家谈谈为什么酷狗音乐自己唱的歌不能下载到本地?,以及为什么酷狗下载的歌曲不是...
华为下载未安装的文件去哪找(华... 今天百科达人给各位分享华为下载未安装的文件去哪找的知识,其中也会对华为下载未安装的文件去哪找到进行解...
家里可以做假山养金鱼吗(假山能... 今天百科达人给各位分享家里可以做假山养金鱼吗的知识,其中也会对假山能放鱼缸里吗进行解释,如果能碰巧解...
四分五裂是什么生肖什么动物(四... 本篇文章极速百科小编给大家谈谈四分五裂是什么生肖什么动物,以及四分五裂打一生肖是什么对应的知识点,希...
怎么往应用助手里添加应用(应用... 今天百科达人给各位分享怎么往应用助手里添加应用的知识,其中也会对应用助手怎么添加微信进行解释,如果能...
美团联名卡审核成功待激活(美团... 今天百科达人给各位分享美团联名卡审核成功待激活的知识,其中也会对美团联名卡审核未通过进行解释,如果能...
一帆风顺二龙腾飞三阳开泰祝福语... 本篇文章极速百科给大家谈谈一帆风顺二龙腾飞三阳开泰祝福语,以及一帆风顺二龙腾飞三阳开泰祝福语结婚对应...