如何制作TParallel。&amp; For循环响应并在TList <t>中存储值?

时间:2017-05-19 13:23:05

标签: multithreading delphi parallel-processing thread-safety

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

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

1 个答案:

答案 0 :(得分:1)

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

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

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

另外,与没有线程的循环进行比较。