加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 大数据 > 正文

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

发布时间:2020-12-15 04:31:06 所属栏目:大数据 来源:网络整理
导读:在我们的Delphi XE4应用程序中,我们使用的是一个OmniThreadPool,MaxExecuting = 4来提高某种计算的效率.不幸的是,我们遇到间歇访问冲突时遇到麻烦(参见例如以下MadExcept错误报告 http://ec2-72-44-42-247.compute-1.amazonaws.com/BugReport.txt).我能够构
在我们的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;

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

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读