How to make a TParallel.&For loop responsive and store values in a TList<T>?

有些话、适合烂在心里 提交于 2019-12-24 08:46:50

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!