5

I have a time consuming routine which I'd like to process in parallel using Delphi XE7's new parallel library.

Here is the single threaded version:

procedure TTerritoryList.SetUpdating(const Value: boolean);
var
  i, n: Integer;
begin
  if (fUpdating <> Value) or not Value then
  begin
    fUpdating := Value;

    for i := 0 to Count - 1 do
    begin
      Territory[i].Updating := Value; // <<<<<< Time consuming routine
      if assigned(fOnCreateShapesProgress) then
        fOnCreateShapesProgress(Self, 'Reconfiguring ' + Territory[i].Name, i / (Count - 1));
    end;
  end;
end;

There is really nothing complex going on. If the territory list variable is changed or set to false then the routine loops around all the sales territories and recreates the territory border (which is the time consuming task).

So here is my attempt to make it parallel:

procedure TTerritoryList.SetUpdating(const Value: boolean);
var
  i, n: Integer;
begin
  if (fUpdating <> Value) or not Value then
  begin
    fUpdating := Value;

    n := Count;
    i := 0;

    TParallel.For(0, Count - 1,
      procedure(Index: integer)
      begin
        Territory[Index].Updating := fUpdating; // <<<<<< Time consuming routine
        TInterlocked.Increment(i);
        TThread.Queue(TThread.CurrentThread,
          procedure
            begin
              if assigned(fOnCreateShapesProgress) then
                fOnCreateShapesProgress(nil, 'Reconfiguring ', i / n);
            end);
      end
    );
  end;
end;

I've replaced the for-loop with a parallel for-loop. The counter, 'i' is locked as it is incremented to show progress. I then wrap the OnCreateShapeProgress event in a TThread.Queue, which will be handled by the main thread. The OnCreateShapeProgress event is handled by a routine which updates the progress bar and label describing the task.

The routine works if I exclude the call to the OnCreateShapeProgress event. It crashes with an EAurgumentOutOfRange error.

So my question is simple:

Am I doing anything anything stupid?

How to do you call an event handler from within a TParallel.For loop or TTask?

4

1 回答 1

4

我能看到的最明显的问题是您排队到工作线程。

您的电话TThread.Queue通行证TThread.CurrentThread。这就是您正在调用的线程TThread.Queue。我认为可以肯定地说你永远不应该传递TThread.CurrentThreadTThread.Queue.

相反,删除该参数。使用仅接受线程过程的一个参数重载。

否则我会注意到进度计数器的增量i并没有真正正确处理。好吧,增量很好,但是您稍后再阅读它,那是一场比赛。如果线程 1 在线程 2 之前递增但线程 2 在线程 1 之前排队进度,则您可以报告无序的进度。通过将计数器递增代码移动到主线程来解决这个问题。只需在排队的匿名方法中增加它。额外的好处是您不再需要使用原子增量,因为所有修改都在主线程上。

除此之外,这份 QC 报告似乎与您报告的内容非常相似:http: //qc.embarcadero.com/wc/qcmain.aspx?d= 128392

最后,AtomicIncrement是在最新版本的 Delphi 中执行无锁递增的惯用方式。

于 2014-11-14T22:18:58.293 回答