gpt4 book ai didi

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

转载 作者:行者123 更新时间:2023-12-03 14:41:45 33 4
gpt4 key购买 nike

在我们的 Delphi XE4 应用程序中,我们使用 MaxExecuting=4 的 OmniThreadPool 来提高某些计算的效率。不幸的是,我们遇到了间歇性访问违规的问题(例如,请参阅以下 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问题。发生的情况是,任务 Controller 有时会在任务本身之前被破坏,从而导致任务访问包含随机数据的内存。

有问题的场景如下([T] 标记任务,[C] 标记任务 Controller ):

  • [T]发送消息
  • [C]收到消息并退出
  • [C] 被摧毁
  • 已创建新任务 [T1] 和 Controller [C1]
  • [T] 尝试退出;在此期间,它访问了由[C]管理的共享内存区域,但随后被属于[C1]或[T1]的数据破坏并覆盖

在 Graymatter 的解决方法中,OnTermminate 为 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 - 为什么以下使用 IOmniThreadPool 的代码会导致访问冲突?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23907144/

33 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com