如何使 TParallel.&For 循环响应并将值存储在 TList 中<T>?



在Delphi 10.1柏林中,我想做一个TParallel.&For循环响应。

我有一个并行循环,类似于有问题的 TParallel.For 中的示例:将值存储在 TList 中,同时在 TParallel.For 循环中计算。循环计算值并将这些值存储在TList<Real>中。

我尝试使用 TTask.Run 在单独的线程中运行TParallel.&For以使其响应:

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
);

但问题仍然存在。我做错了什么?

Parallel.For 循环中的所有线程共享Res变量。当线程即将将Res值存储到列表中时,它可能已被一个或多个线程更改。换句话说,Res的值在将其放入列表时是不可预测的。

通过将Res设置为每个线程的本地来修复它。

至于哪种方法最好,我建议进行性能比较。@Ken的建议似乎也是一个不错的主意。避免锁定通常是获得良好性能的秘诀。

此外,还要与没有线程的循环进行比较。

相关内容

最新更新