在我们的Delphi XE4应用程序中,我们使用的是一个OmniThreadPool,MaxExecuting = 4来提高某种计算的效率.不幸的是,我们遇到间歇访问冲突时遇到麻烦(参见例如以下MadExcept错误报告
@L_404_0@).我能够构建以下示例来演示问题.运行以下控制台应用程序后,System.SyncObjs.TCriticalSection.Acquire中的访问冲突通常在一分钟内发生.有人可以告诉我在下面的代码中我做错了什么,还是告诉我另一种达到理想结果的方法?
program OmniPoolCrashTest; {$APPTYPE CONSOLE} uses Winapi.Windows,System.SysUtils,DSiWin32,GpLists,OtlSync,OtlThreadPool,OtlTaskControl,OtlComm,OtlTask; const cTimeToWaitForException = 10 * 60 * 1000; // program exits if no exception after 10 minutes MSG_CALLEE_FINISHED = 113; // our custom Omni message ID cMaxAllowedParallelCallees = 4; // enforced via thread pool cCalleeDuration = 10; // 10 miliseconds cCallerRepetitionInterval = 200; // 200 milliseconds cDefaultNumberOfCallers = 10; // 10 callers each issuing 1 call every 200 milliseconds var gv_OmniThreadPool : IOmniThreadPool; procedure OmniTaskProcedure_Callee(const task: IOmniTask); begin Sleep(cCalleeDuration); task.Comm.Send(MSG_CALLEE_FINISHED); end; procedure PerformThreadPoolTest(); var OmniTaskControl : IOmniTaskControl; begin OmniTaskControl := CreateTask(OmniTaskProcedure_Callee).Schedule(gv_OmniThreadPool); WaitForSingleObject(OmniTaskControl.Comm.NewMessageEvent,INFINITE); end; procedure OmniTaskProcedure_Caller(const task: IOmniTask); begin while not task.Terminated do begin PerformThreadPoolTest(); Sleep(cCallerRepetitionInterval); end; end; var CallerTasks : TGpInterfaceList<IOmniTaskControl>; i : integer; begin gv_OmniThreadPool := CreateThreadPool('CalleeThreadPool'); gv_OmniThreadPool.MaxExecuting := cMaxAllowedParallelCallees; CallerTasks := TGpInterfaceList<IOmniTaskControl>.Create(); for i := 1 to StrToIntDef(ParamStr(1),cDefaultNumberOfCallers) do begin CallerTasks.Add( CreateTask(OmniTaskProcedure_Caller).Run() ); end; Sleep(cTimeToWaitForException); for i := 0 to CallerTasks.Count-1 do begin CallerTasks[i].Terminate(); end; CallerTasks.Free(); end.
解决方法
你在这里有一个难以找到的
Task controller needs an owner问题的例子.发生什么是任务控制器有时在任务本身之前被破坏,并导致任务访问包含随机数据的内存.
有问题的情况就是这样([T]标记任务,[C]标记任务控制器):
> [T]发送消息
> [C]接收消息并退出
> [C]被破坏
>新建任务[T1]和控制器[C1]
> [T]试图退出;在此期间,它访问由[C]管理的共享存储区,然后被属于[C1]或[T1]的数据销毁并覆盖
在Graymatter的解决方法中,OnTerminated为OmniThreadLibrary中的“解决”问题的任务创建了一个隐含的所有者.
正确的等待任务完成的方法是调用taskControler.WaitFor.
procedure OmniTaskProcedure_Callee(const task: IOmniTask); begin Sleep(cCalleeDuration); end; procedure PerformThreadPoolTest(); var OmniTaskControl : IOmniTaskControl; begin OmniTaskControl := CreateTask(OmniTaskProcedure_Callee).Schedule(gv_OmniThreadPool); OmniTaskControl.WaitFor(INFINITE); end;