delphi – 为什么使用IOmniThreadPool的以下代码会导致访问冲突?

前端之家收集整理的这篇文章主要介绍了delphi – 为什么使用IOmniThreadPool的以下代码会导致访问冲突?前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
在我们的Delphi XE4应用程序中,我们使用的是一个OmniThreadPool,MaxExecuting = 4来提高某种计算的效率.不幸的是,我们遇到间歇访问冲突时遇到麻烦(参见例如以下MadExcept错误报告 http://ec2-72-44-42-247.compute-1.amazonaws.com/BugReport.txt).我能够构建以下示例来演示问题.运行以下控制台应用程序后,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;

我将考虑用引用计数的解决方案替换共享内存记录,这将解决这种问题(或者至少使它们更容易找到).

猜你在找的Delphi相关文章