使用Delphi XE7并行库 [英] Using the Delphi XE7 Parallel Library

查看:190
本文介绍了使用Delphi XE7并行库的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个耗时的程序,我想使用Delphi XE7的新的并行库并行处理。



这是单线程版本:

  procedure TTerritoryList.SetUpdating(const Value:boolean); 
var
i,n:整型;
begin
如果(fUpdating<> Value)或不是值然后
begin
fUpdating:= Value;

for i:= 0 to Count - 1 do
begin
Territory [i] .Updating:= Value; //<<<<< (fOnCreateShapesProgress)然后
fOnCreateShapesProgress(Self,'Reconfiguring'+ Territory [i] .Name,i /(Count-1));
结束
结束
结束

真的没有什么复杂的。如果领域列表变量被更改或设置为false,则例程将循环遍布所有销售区域并重新创建领域边界(这是耗时的任务)。



所以这是我尝试使它平行:

  procedure TTerritoryList.SetUpdating(const Value:boolean); 
var
i,n:整数;
begin
如果(fUpdating<> Value)或不是值然后
begin
fUpdating:= Value;

n:= Count;
i:= 0;

TParallel.For(0,Count - 1,
procedure(Index:integer)
begin
Territory [Index] .Updating:= fUpdating; //< ;<<<<时间耗费例程
TInterlocked.Increment(i);
TThread.Queue(TThread.CurrentThread,
procedure
begin
如果分配(fOnCreateShapesProgress)然后
fOnCreateShapesProgress(nil,'Reconfiguring',i / n);
end);
end
);
结束
结束

我用并行for循环替换了for-loop。柜台,'我'被锁定,因为它增加显示进度。然后我将OnCreateShapeProgress事件包装在一个TThread.Queue中,该事件将由主线程处理。 OnCreateShapeProgress事件由更新描述任务的进度条和标签的例程来处理。



如果我将OnCreateShapeProgress事件的调用排除在外,该例程将工作。它崩溃了一个EAurgumentOutOfRange错误。



所以我的问题很简单:



我做任何愚蠢的事情?



如何从TParallel.For循环或TTask中调用事件处理程序?

解决方案

我可以看到的最明显的问题是你排队到工作线程。



您致电 TThread.Queue 通过 TThread.CurrentThread 。那就是你正在调用 TThread.Queue 的线程。我觉得你不应该通过 TThread.CurrentThread TThread.Queue 是安全的。



而是删除该参数。使用刚接受线程程序的一个参数重载。



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



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



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


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?

解决方案

The most obvious problem that I can see is that you queue to the worker thread.

Your call to TThread.Queue passes TThread.CurrentThread. That is the very thread on which you are calling TThread.Queue. I think it is safe to say that you should never pass TThread.CurrentThread to TThread.Queue.

Instead, remove that parameter. Use the one parameter overload that just accepts a thread procedure.

Otherwise I'd note that the incrementing of the progress counter i is not really handled correctly. Well, the incrementing is fine, but you then read it later and that's a race. You can report progress out of order if thread 1 increments before thread 2 but thread 2 queues progress before thread 1. Solve that by moving the counter increment code to the main thread. Simply increment it inside the queued anonymous method. Added bonus to that is you no longer need to use an atomic increment since all modifications are on the main thread.

Beyond that, this QC report seems rather similar to what you report: http://qc.embarcadero.com/wc/qcmain.aspx?d=128392

Finally, AtomicIncrement is the idiomatic way to perform lock free incrementing in the latest versions of Delphi.

这篇关于使用Delphi XE7并行库的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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