Using the Delphi XE7 Parallel Library

I have a laborious procedure that I would like to process in parallel using the new parallel Delphi XE7 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; 

In fact, nothing complicated happens. If the variable in the list of territories is changed or set to false, then the procedure will go around all sales territories and recreate the border of the territory (which is a laborious 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 replaced the for-loop with a parallel for-loop. Counter "i" is locked as it increases to indicate progress. I then end the OnCreateShapeProgress event in TThread.Queue, which will be handled by the main thread. The OnCreateShapeProgress event is handled by a routine that updates the progress bar and the label that describes the task.

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

So my question is simple:

Am I doing anything stupid?

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

+5
source share
1 answer

The most obvious problem that I see is that you are queuing up on a workflow.

Your call to TThread.Queue passes through TThread.CurrentThread . This is the thread you are calling TThread.Queue . I think it's safe to say that you should never go through TThread.CurrentThread before TThread.Queue .

Instead, remove this option. Use one parameter overload, which simply accepts a stream procedure.

Otherwise, I would have noticed that the increment of the progress counter i is not actually performed correctly. Well, the increment is fine, but then you read it later and this race. You can report progress out of order if stream 1 increases to stream 2, but stream 2 queues progress to stream 1. Solve this by moving the counter increment code to the main stream. Just add it to the queue of the anonymous method. Added bonus to this - you no longer need to use atomic increment, since all changes are in the main stream.

Also, this QC report seems pretty similar to what you are reporting: http://qc.embarcadero.com/wc/qcmain.aspx?d=128392

Finally, AtomicIncrement is an idiomatic way to lock without restrictions in recent versions of Delphi.

+4
source

Source: https://habr.com/ru/post/1206920/


All Articles