Question

I am learning how to use OmniThreadLibrary in Delphi XE2, i wonder if someone can show me how to cancel a parallel.foreach.

I read that I should use a cancellation token, but I cannot find an example of some sort on how to use it.

This is the original for loop inside the function.

function SomeFunction() : string;
begin

  for value :=  0 to length(listOfThings)-1 do
  begin

    Chain := Function1( listOfThings[value] );

    if Evaluate( Chain , Solution) then
      Parameters[value] := Solution
    else
    begin
      Result := 'ERROR';
      exit;
    end;
  end;
end;

And this is how I am using the Parallel.ForEach

function SomeFunction() : string;
begin

  Parallel.ForEach(0, length(listOfThings)-1 ).Execute(

    procedure (const value: integer)
        var Chain : string;
        begin
          Chain := Function1(listOfThings[value]);

        if Evaluate(Chain , Solution) then
          Parameters[value] := Solution
        else
          begin
            Result := 'ERROR';    //Here is where it won't work
            exit;  
          end;
        end
  );
end;

Inside the Parallel.ForEach I can't do Result := 'ERROR' because it is not captured inside the procedure, so I think if I can cancel the Parallel.ForEach and report that cancellation, then I can just assign Result := 'ERROR' outside.

But I am new to OmniThreadLibrary and I don't know how to do such a thing, please help me :)

Was it helpful?

Solution

You need to use a cancellation token:

var
  cancelToken: IOmniCancellationToken;

You obtain the cancellation token by calling CreateOmniCancellationToken from the OtlSync unit.

cancelToken := CreateOmniCancellationToken;

You then supply the token to the parallel loop:

Parallel.ForEach(...)
    .CancelWith(cancelToken)
    .Execute(...);

And you signal the cancellation token by calling its Signal method.

cancelToken.Signal;

From outside the parallel loop you can use

cancelToken.IsSignaled

to detect that you cancelled. Or you can capture a boolean variable from the surrounding scope and pass the information through that variable.

The example here gives an illustration.

OTHER TIPS

The cancellation token is only half of it. If you need it to return a value, you'll need to use Aggregate, because you can have an arbitrary number of elements in the sequence, but only one return value, so you need to collapse (aggregate) an arbitrary number of return values into one final value. So you want something like this:

function SomeFunction() : string;
var
  cancelToken: IOmniCancellationToken;
  error: TOmniValue;
begin
  cancelToken := CreateOmniCancellationToken;
  error := Parallel.ForEach(0, length(listOfThings)-1 ).
    CancelWith(cancelToken).
    Aggregate('',
      procedure(var aggregate: TOmniValue; const value: TOmniValue)
      var Chain : string;
      begin
        Chain := Function1(listOfThings[value]);

      if Evaluate(Chain , Solution) then
        Parameters[value] := Solution
      else
        begin
          aggregate := 'ERROR';
          cancelToken.signal;
        end;
      end).
  Execute(
    procedure(const value: TOmniValue; var result: TOmniValue)
    begin
      if value <> '' then
        result := value;
    end);

  if error <> '' then
    //something went wrong
end;

This may not be exactly perfect, but it should get you on the right track.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top