使用异步调用的delphi线程池示例

这是我的下一个测试项目,看看Delphi线程库最适合我的“文件扫描”任务,我想在多线程/线程池中处理。...

这是我的下一个测试项目,看看Delphi线程库最适合我的“文件扫描”任务,我想在多线程/线程池中处理。

Man using multiple screens to work on coding and programming.

重复我的目标:将500-2000多个文件的顺序“文件扫描”从非线程方法转换为线程方法。我不应该一次运行500个线程,因此我想使用一个线程池。线程池是一个类似队列的类,它为许多正在运行的线程提供队列中的下一个任务。

第一次(非常基本的)尝试是通过简单地扩展TThread类并实现Execute方法(我的线程字符串解析器)来实现的。

由于Delphi没有现成实现的线程池类,在我的第二次尝试中,我尝试使用Primoz Gabrijelcic的OmniThreadLibrary。

OTL非常棒,它有无数种在后台运行任务的方法,如果您想使用“启动并忘记”的方法来处理代码片段的线程执行,那么这是一种可行的方法。

andreas Hausladden的异步调用

Note: what follows would be more easy to follow if you first download the source code.

在探索以线程方式执行某些函数的更多方法的同时,我还决定尝试Andreas Hausladden开发的“AsyncCalls.pas”单元。Andy的AsyncCalls–Asynchronous function calls unit是另一个库,Delphi开发人员可以使用它来减轻实现线程化方法来执行某些代码的痛苦。

来自Andy的博客:使用AsyncCalls,您可以同时执行多个函数,并在启动它们的函数或方法中的每个点同步它们。。。AsyncCalls单元提供多种函数原型来调用异步函数。。。它实现了一个线程池!安装非常简单:只需使用来自任何单元的异步调用,就可以立即访问“在单独的线程中执行,同步主UI,等待完成”之类的内容。

除了免费使用(MPL许可证)异步调用之外,Andy还经常发布他自己对Delphi IDE的修复,如“Delphi加速”和“DDevExtensions”,我相信您一定听说过(如果还没有使用)。

异步调用正在运行

本质上,所有AsyncCall函数都返回一个IAsyncCall接口,该接口允许同步函数。IASNycall公开了以下方法:

//v 2.98 of asynccalls.pas IAsyncCall = interface //waits until the function is finished and returns the return value function Sync: Integer; //returns True when the asynchron function is finished function Finished: Boolean; //returns the asynchron function's return value, when Finished is TRUE function ReturnValue: Integer; //tells AsyncCalls that the assigned function must not be executed in the current threa procedure ForceDifferentThread; end; Here's an example call to a method expecting two integer parameters (returning an IAsyncCall): TAsyncCalls.Invoke(AsyncMethod, i, Random(500)); function TAsyncCallsForm.AsyncMethod(taskNr, sleepTime: integer): integer;begin result := sleepTime; Sleep(sleepTime); TAsyncCalls.VCLInvoke( procedure begin Log(Format('done > nr: %d / tasks: %d / slept: %d', [tasknr, asyncHelper.TaskCount, sleepTime])); end);end; The TAsyncCalls.VCLInvoke is a way to do synchronization with your main thread (application's main thread - your application user interface). VCLInvoke returns immediately. The anonymous method will be executed in the main thread. There's also VCLSync which returns when the anonymous method was called in the main thread.

Thread Pool in AsyncCalls

Back to my "file scanning" task: when feeding (in a for loop) the asynccalls thread pool with series of TAsyncCalls.Invoke() calls, the tasks will be added to internal the pool and will get executed "when time comes" (when previously added calls have finished).

Wait All IAsyncCalls To Finish

The AsyncMultiSync function defined in asnyccalls waits for the async calls (and other handles) to finish. There are a few overloaded ways to call AsyncMultiSync, and here's the simplest one: function AsyncMultiSync(const List: array of IAsyncCall; WaitAll: Boolean = True; Milliseconds: Cardinal = INFINITE): Cardinal; If I want to have "wait all" implemented, I need to fill in an array of IAsyncCall and do AsyncMultiSync in slices of 61.

My AsnycCalls Helper

Here's a piece of the TAsyncCallsHelper: WARNING: partial code! (full code available for download)uses AsyncCalls;type TIAsyncCallArray = array of IAsyncCall; TIAsyncCallArrays = array of TIAsyncCallArray; TAsyncCallsHelper = class private fTasks : TIAsyncCallArrays; property Tasks : TIAsyncCallArrays read fTasks; public procedure AddTask(const call : IAsyncCall); procedure WaitAll; end; WARNING: partial code!procedure TAsyncCallsHelper.WaitAll;var i : integer;begin for i := High(Tasks) downto Low(Tasks) do begin AsyncCalls.AsyncMultiSync(Tasks[i]); end;end; This way I can "wait all" in chunks of 61 (MAXIMUM_ASYNC_WAIT_OBJECTS) - i.e. waiting for arrays of IAsyncCall. With the above, my main code to feed the thread pool looks like: procedure TAsyncCallsForm.btnAddTasksClick(Sender: TObject);const nrItems = 200;var i : integer;begin asyncHelper.MaxThreads := 2 * System.CPUCount; ClearLog('starting'); for i := 1 to nrItems do begin asyncHelper.AddTask(TAsyncCalls.Invoke(AsyncMethod, i, Random(500))); end; Log('all in'); //wait all //asyncHelper.WaitAll; //or allow canceling all not started by clicking the "Cancel All" button: while NOT asyncHelper.AllFinished do Application.ProcessMessages; Log('finished');end;

Cancel all? - Have To Change The AsyncCalls.pas :(

I would also like to have a way of "cancelling" those tasks that are in the pool but are waiting for their execution. Unfortunately, the AsyncCalls.pas does not provide a simple way of canceling a task once it has been added to the thread pool. There's no IAsyncCall.Cancel or IAsyncCall.DontDoIfNotAlreadyExecuting or IAsyncCall.NeverMindMe. For this to work I had to change the AsyncCalls.pas by trying to alter it as less as possible - so that when Andy releases a new version I only have to add a few lines to have my "Cancel task" idea working. Here's what I did: I've added a "procedure Cancel" to the IAsyncCall. The Cancel procedure sets the "FCancelled" (added) field which gets checked when the pool is about to start executing the task. I needed to slightly alter the IAsyncCall.Finished (so that a call reports finished even when cancelled) and the TAsyncCall.InternExecuteAsyncCall procedure (not to execute the call if it has been cancelled). You can use WinMerge to easily locate differences between Andy's original asynccall.pas and my altered version (included in the download). You can download the full source code and explore.

Confession

NOTICE! :)

The CancelInvocation method stopps the AsyncCall from being invoked. If the AsyncCall is already processed, a call to CancelInvocation has no effect and the Canceled function will return False as the AsyncCall wasn't canceled.The Canceled method returns True if the AsyncCall was canceled by CancelInvocation.The Forget method unlinks the IAsyncCall interface from the internal AsyncCall. This means that if the last reference to the IAsyncCall interface is gone, the asynchronous call will be still executed. The interface's methods will throw an exception if called after calling Forget. The async function must not call into the main thread because it could be executed after the TThread.Synchronize/Queue mechanism was shut down by the RTL what can cause a dead lock. Note, though, that you can still benefit from my AsyncCallsHelper if you need to wait for all async calls to finish with "asyncHelper.WaitAll"; or if you need to "CancelAll".

  • 发表于 2021-09-21 23:56
  • 阅读 ( 296 )
  • 分类:编程

你可能感兴趣的文章

用java编写多线程代码的4种方法

...其中的几个选项,以便您可以更好地判断下一个Java项目使用哪个选项。 ...

  • 发布于 2021-03-14 04:48
  • 阅读 ( 177 )

同步(synchronous)和java中的异步调用(asynchronous calls in java)的区别

...击按钮或从互联网获取数据后执行某些操作时,我们可以使用它们。接口有助于在Java中实现回调。回调有两种类型,即同步调用和异步调用。 覆盖的关键领域 1.如何实现回调函数–步骤2.什么是Java中的同步调用–功能,程序3....

  • 发布于 2021-07-01 14:57
  • 阅读 ( 825 )

如何将媒体文件嵌入delphi可执行文件(rc/.res)(embed media files into a delphi executable (rc/.res))

使用声音和动画等多媒体文件的游戏和其他类型的应用程序必须随应用程序一起分发额外的多媒体文件,或者将文件嵌入可执行文件中。 您可以将原始数据作为资源添加到应用程序中,而不是分发单独的文件供应用程序使用...

  • 发布于 2021-09-06 15:08
  • 阅读 ( 191 )

理解delphi项目和单元源文件

...含可由所有应用程序表单共享的通用源代码信息。 除非使用包含程序图标和版本信息的Windows资源文件(RES),否则无法编译Delphi项目。它还可能包含其他资源,如图像、表格、光标等。RES文件由Delphi自动生成。 注意:以DPR...

  • 发布于 2021-09-06 15:15
  • 阅读 ( 173 )

delphi中的sql

...找数据位置等。 delphi中的tquery 如果要在应用程序中使用SQL,您将非常熟悉TQuery组件。Delphi使您的应用程序能够通过TQuery组件直接使用SQL语法来访问Paradox和dBase表(使用本地SQL—ANSI标准SQL的子集)、本地InterBase服务器上的数...

  • 发布于 2021-09-08 10:40
  • 阅读 ( 184 )

优化delphi程序的内存使用

01 2006年 windows对程序的内存使用有何看法? 请看Windows任务管理器的屏幕截图。。。 最右边的两列表示CPU(时间)使用情况和内存使用情况。如果某个进程严重影响其中任何一个,则系统将减速。 经常影响CPU使用率的...

  • 发布于 2021-09-15 03:43
  • 阅读 ( 198 )

用delphi截取键盘输入

...。 请注意,只有一件事:此代码绝不限于仅与TImage一起使用。 KeyboardHookProc功能用作通用的KeyPreview和KeyProcess机制。

  • 发布于 2021-09-15 03:46
  • 阅读 ( 154 )

理解delphi中的内存分配

...lt := 1 + DoStackOverflow; end; 这个“堆栈”是什么?为什么使用上面的代码会出现溢出? 因此,DoStackOverflow函数递归地调用自己——没有“退出策略”——它只是继续旋转,从不退出。 您可以做的一个快速修复方法是清除明显...

  • 发布于 2021-09-15 03:47
  • 阅读 ( 191 )

使用delphi性能计数器精确测量运行时间

...是,Delphi提供了一个高性能计数器来限定这些时间。 使用rtl的now函数 一个选项使用Now函数。现在,在SysUtils单元中定义,返回当前系统日期和时间。 几行代码测量某个进程的“开始”和“停止”之间经过的时间: Now函数...

  • 发布于 2021-09-15 03:49
  • 阅读 ( 179 )

如何用delphi搜索文件和文件夹(search for files and folders with delphi)

...件夹中搜索通常是有用且必要的。在这里,您将看到如何使用Delphi的强大功能创建一个简单但功能强大的查找所有匹配文件项目。 文件/文件夹掩码搜索项目 下面的项目不仅允许您通过子文件夹搜索文件,还允许您轻松确定...

  • 发布于 2021-09-15 03:52
  • 阅读 ( 211 )
ihwd1824
ihwd1824

0 篇文章

相关推荐