如何使TParallel.& For循环响应并将值存储在TList< T>中? [英] How to make a TParallel.&For loop responsive and store values in a TList<T>?

查看:103
本文介绍了如何使TParallel.& For循环响应并将值存储在TList< T>中?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在Delphi 10.1 Berlin中,我想使TParallel.&For循环响应.

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>.

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

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
....

我也尝试了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
);

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

but the problem remains. What am I doing wrong?

推荐答案

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

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.

通过在每个线程中本地设置Res来修复该问题.

Fix it by making Res local to each thread.

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

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.

这篇关于如何使TParallel.&amp; For循环响应并将值存储在TList&lt; T&gt;中?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆