为了尝试在Delphi中使用线程库并行计算任务,并使用TTask.WaitForAny()
获取第一个计算结果,有时会发生异常导致执行停止。
异常时的调用堆栈:
在$752D2F71处出现首次机会异常。异常类EMonitorLockException,消息为“Object lock not owned”。进程Project1.exe (11248)
:752d2f71 KERNELBASE.RaiseException + 0x48
System.TMonitor.CheckOwningThread
System.ErrorAt(25,$408C70)
System.Error(reMonitorNotLocked)
System.TMonitor.CheckOwningThread
System.TMonitor.Exit
System.TMonitor.Exit($2180E40)
System.Threading.TTask.RemoveCompleteEvent(???)
System.Threading.TTask.DoWaitForAny((...),4294967295)
System.Threading.TTask.WaitForAny((...))
Project9.Parallel2
Project9.Project1
:74ff919f KERNEL32.BaseThreadInitThunk + 0xe
:7723b54f ntdll.RtlInitializeExceptionChain + 0x8f
:7723b51a ntdll.RtlInitializeExceptionChain + 0x5a
调用栈表明异常是由线程库中的一个 bug 或者 TMonitor
和/或 TTask.WaitForAny()
引起的。为了验证这一点,代码被简化到最小限度:
program Project1;
{$APPTYPE CONSOLE}
uses
System.SysUtils, System.Threading, System.Classes, System.SyncObjs,
System.StrUtils;
var
WorkerCount : integer = 1000;
function MyTaskProc: TProc;
begin
result := procedure
begin
// Do something
end;
end;
procedure Parallel2;
var
i : Integer;
Ticks: Cardinal;
tasks: array of ITask;
LTask: ITask;
workProc: TProc;
begin
workProc := MyTaskProc();
Ticks := TThread.GetTickCount;
SetLength(tasks, WorkerCount); // number of parallel tasks to undertake
for i := 0 to WorkerCount - 1 do // parallel tasks
tasks[i] := TTask.Run(workProc);
TTask.WaitForAny(tasks); // wait for the first one to finish
for LTask in tasks do
LTask.Cancel; // kill the remaining tasks
Ticks := TThread.GetTickCount - Ticks;
WriteLn('Parallel time ' + Ticks.ToString + ' ms');
end;
begin
try
repeat
Parallel2;
WriteLn('finished');
until FALSE;
except
on E: Exception do
writeln(E.ClassName, ': ', E.Message);
end;
Readln;
end.
现在,该错误在一段时间后就会重现,RTL错误已经得到确认。
这被提交到Embarcadero作为RSP-10197 TTask.WaitForAny gives exception EMonitorLockException "Object lock not owned"。
考虑到目前使用Delphi线程库无法解决此问题,问题是:
是否有一种解决方法可以并行执行一个过程以获得第一个获得的解决方案?