How to set up TPalallel. & For, react and store values โ€‹โ€‹in TList <T>?

In Delphi 10.1 Berlin, I would like to make the loop TParallel.&For

responsive.

I have a parallel loop similar to the TParallel.For example in question: Store values โ€‹โ€‹in a TList as they are computed in a TParallel.For loop . The loop calculates the values โ€‹โ€‹and stores those values โ€‹โ€‹in TList<Real>

.

I am trying to run TParallel.&For

on 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 wrong 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 a part, Lock.Enter

Lock.Leave

I also triedSynchronize

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


source to share


1 answer


All threads in the loop Parallel.For

share a variable Res

. When a thread is about to store a value Res

in the list, it may already have been modified by one or more threads. In other words, the value is Res

unpredictable as it is entered into the list.

Fix it by making it Res

local to each thread.



As for which method is the best, I suggest comparing performance. And @Ken's suggestion seems like a good idea to give it a try. Blocking prevention is often a recipe for good performance.

Also, compare to a loop without threads.

+1


source







All Articles