2

I need to exit a TParallel.For loop in the fastest possible way when the user clicks a Cancel button or when the user closes/destroys the form. I have tried both with TParallel.TLoopState.Stop and TParallel.TLoopState.Break:

var
  BreakCondition: Boolean;

procedure TForm2.DoStartLoop;
begin
  BreakCondition := False;
  System.Threading.TParallel.For(1, 50,
    procedure(idx: Integer; LS: TParallel.TLoopState)
    begin
      if BreakCondition then
      begin
        //LS.&BREAK;
        LS.STOP;
        //EXIT;
      end
      else
        DoProcessValue(idx);
    end);
end;

Unfortunately, the Embarcadero documentation for TParallel.TLoopState.Stop and TParallel.TLoopState.Break states only:

Embarcadero Technologies does not currently have any additional information.

I have also the impression that the loop is not interrupted very quickly. Is there a better way?

user1580348
  • 5,721
  • 4
  • 43
  • 105
  • 1
    "If control of the iteration itself is needed from the iterator event, the iterator event handler should be one using the TParallel.TLoopState parameter. When present, the event handler will be given an instance of TParallel.TLoopState from which state information from Faulted,Stopped, or ShouldExit can be monitored, or the iteration loop itself can be controlled with the Break or Stop methods." – LU RD May 04 '17 at 19:13
  • And that is stated in the [`TParallel.For` documentation](http://docwiki.embarcadero.com/Libraries/en/System.Threading.TParallel.For) – Remy Lebeau May 04 '17 at 19:16
  • Here is an example: [How can I use TTask.WaitForAny from the new threading library?](http://stackoverflow.com/a/29078846/576719) – LU RD May 04 '17 at 19:23
  • To stop other threading tasks quickly, your `DoProcessValue()` should check the `Stopped` property regularly. – LU RD May 04 '17 at 19:31
  • I've used a simple `TEvent`, when the user cancels its set to signaled and the loop checks that and exits. There are only so many "does not currently have any additional information.." worth following. – FredS May 05 '17 at 02:48

1 Answers1

4

From the TParallel.For documentation:

If control of the iteration itself is needed from the iterator event, the iterator event handler should be one using the TParallel.TLoopState parameter. When present, the event handler will be given an instance of TParallel.TLoopState from which state information from Faulted,Stopped, or ShouldExit can be monitored, or the iteration loop itself can be controlled with the Break or Stop methods.

The way to keep track of LoopState is to use a method with the following signature:

TIteratorStateEvent = 
  procedure (Sender: TObject; AIndex: Integer; const LoopState: TLoopState) of object;

Or use its anonymous version:

class function &For(AStride, ALowInclusive, AHighInclusive: Integer; 
const AIteratorEvent: TProc<Integer, TLoopState>; APool: TThreadPool): TLoopResult;
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

If the documentation fails the easiest way is to search the source code for the class, or let code completion do the work.

TLoopState = class
    private [...]
    public
      procedure Break;
      procedure Stop;
      function ShouldExit: Boolean;  <<-- this looks useful

      property Faulted: Boolean read GetFaulted;
      property Stopped: Boolean read GetStopped;  <<-- or this
      property LowestBreakIteration: Variant read GetLowestBreakIteration;
    end;

Example:

procedure TForm1.btnParallelForClick(Sender: TObject);
var
  Tot: Integer;
  SW: TStopwatch;
begin
     try
     // counts the prime numbers below a given value
       Tot :=0;
       SW :=TStopWatch.Create;
       SW.Start;
       //Use a method that supports LoopState
       TParallel.For(2,1,Max,procedure(I:Int64; State: TLoopState)
       begin
         //check loopstate every now and again.
         if State.ShouldExit then exit;  
         if IsPrime(I) then TInterlocked.Increment(Tot);
       end);
     SW.Stop;
      Memo1.Lines.Add(Format('Parallel For loop. Time (in milliseconds): %d - Primes found: %d', [SW.ElapsedMilliseconds,Tot]));
     except on E:EAggregateException do
      ShowMessage(E.ToString);
     end;
end;
Johan
  • 74,508
  • 24
  • 191
  • 319