在Delphi 10.1柏林中,我想让TParallel.&For
循环响应。
我有一个类似于问题TParallel.For: Store values in a TList while they are calculated in a TParallel.For loop中的示例的并行循环。循环计算值并将这些值存储在TList<Real>
中。
我尝试在TParallel.&For
的单独帖子中运行TTask.Run
以使其响应:
type
TCalculationProject=class(TObject)
private
Task: ITask;
...
public
List: TList<Real>;
...
end;
function TCalculationProject.CalculateListItem(const AIndex: Integer): Real;
begin
//a function which takes a lot of calculation time
//however in this example we simulate the calculation time and
//use a simple alogorithm to verify the list afterwards
Sleep(30);
Result:=10*AIndex;
end;
procedure TCalculationProject.CalculateList;
begin
List.Clear;
Task:=TTask.Run(
procedure
var
LoopResult: TParallel.TLoopResult;
Res: Real;
Lock: TCriticalSection;
begin
Lock:=TCriticalSection.Create;
try
LoopResult:=TParallel.&For(0, 1000-1,
procedure(AIndex: Integer; LoopState: TParallel.TLoopState)
begin
Res:=CalculateListItem(AIndex);
Lock.Enter;
try
List.Add(Res);
finally
Lock.Leave;
end;
end
);
finally
Lock.Free;
end;
if LoopResult.Completed then
begin
TThread.Synchronize(TThread.Current,
procedure
begin
SortList;
ShowList;
end
);
end;
end
);
end;
问题是列表随机不正确:列表中有重复的值。例如:
list item 0: 0
list item 1: 10
list item 2: 20
list item 3: 20 <- incorrect
list item 4: 20 <- incorrect
list item 5: 50
....
而不是Lock.Enter
Lock.Leave
部分,我也试过了Synchronize
TThread.Synchronize(TThread.Current,
procedure
begin
List.Add(Res);
end
);
或
TThread.Synchronize(nil,
procedure
begin
List.Add(Res);
end
);
和Queue
TThread.Queue(TThread.Current,
procedure
begin
List.Add(Res);
end
);
或
TThread.Queue(nil,
procedure
begin
List.Add(Res);
end
);
但问题仍然存在。我做错了什么?
答案 0 :(得分:1)
Find file
循环中的所有线程共享Parallel.For
变量。当一个线程即将Res
值存储到列表中时,它可能已被一个或多个线程更改。换句话说,Res
的值在将其放入列表时是不可预测的。
通过为每个线程设置Res
本地来修复它。
至于哪种方法最好,我建议进行性能比较。 @Ken的建议似乎也是一个好主意。避免锁定通常是获得良好性能的方法。
另外,与没有线程的循环进行比较。