问题
In Delphi 10.1 Berlin I would like to make a TParallel.&For
loop responsive.
I have a parallel loop similar to the example in question TParallel.For: Store values in a TList while they are calculated in a TParallel.For loop . The loop calculates values and stores these values in a TList<Real>
.
I try to run the TParallel.&For
in a separate thread with TTask.Run
to make it responsive:
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;
The problem is that the list is incorrect on a random basis: there are duplicate values in the list. For example:
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
....
Instead of the Lock.Enter
Lock.Leave
part I have also tried Synchronize
TThread.Synchronize(TThread.Current,
procedure
begin
List.Add(Res);
end
);
or
TThread.Synchronize(nil,
procedure
begin
List.Add(Res);
end
);
and Queue
TThread.Queue(TThread.Current,
procedure
begin
List.Add(Res);
end
);
or
TThread.Queue(nil,
procedure
begin
List.Add(Res);
end
);
but the problem remains. What am I doing wrong?
回答1:
All threads in the Parallel.For
loop shares the Res
variable. When a thread is about to store the Res
value into the list, it could already have been altered by one or many threads. In other words, the value of Res
is unpredictable at the time of putting it into the list.
Fix it by making Res
local to each thread.
As for which method is the best, I suggest to make a performance comparison. And the suggestion by @Ken seems like a good idea to try as well. Avoiding locks is often a recipe for good performance.
Also, compare against a loop without threads as well.
来源:https://stackoverflow.com/questions/44071313/how-to-make-a-tparallel-for-loop-responsive-and-store-values-in-a-tlistt