2014-11-14 40 views
5

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

这里是单线程版本:

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; 

实在没有什么复杂的事情。如果地区列表变量被更改或设置为false,则该例程在所有销售地区周围循环并重新创建地区边界(这是耗时的任务)。

因此,这里是我的尝试,使之并行:

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; 

我把它换成for循环与并行for循环。计数器'i'被锁定,因为它会增加以显示进度。然后我将OnCreateShapeProgress事件包装在一个TThread.Queue中,由主线程处理。 OnCreateShapeProgress事件由更新描述任务的进度条和标签的例程处理。

如果我排除对OnCreateShapeProgress事件的调用,则该例程有效。它与EAurgumentOutOfRange错误崩溃。

所以我的问题很简单:

我在做什么蠢事?

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

+0

'TParallel.For'在XE7中有一个错误,在更新1中已修复。我认为它在这里没有任何意义,但是仍然使用更新进行测试。请参阅[TParallel.For保留区,但不能释放内存](https://quality.embarcadero.com/browse/RSP-9564)。 – 2014-11-14 22:57:31

回答

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是执行在德尔福最新版本的无锁递增的习惯的方法。

+0

谢谢大卫 - 这很有帮助。传递'TThread.CurrentThread'是Danny Wind在最近的Coderage 9谈话(21分钟25秒)中所做的。我同意这看起来不正确。当我将它改为'nil'时,它不会崩溃,但不会显示进度条。当我将'TThread.Queue'改为'TThread.Syncronize'时,它会给出一个'EArgumentOutOfRange'错误。 – 2014-11-15 03:16:56

+0

看起来好像是在使用'TThread,Queue'时,代码只在例程结束后才执行(哪种类型是有意义的)。所以改为'TThread.Syncronize'应该可以解决这个问题,但我仍然会遇到错误。 – 2014-11-15 03:32:16

+0

您应该使用Queue的一个参数重载。 – 2014-11-15 08:12:20

相关问题