如何在Delphi xe2中使用OTL终止parallel.foreach?

4

我正在学习如何在Delphi XE2中使用OmniThreadLibrary,我想知道是否有人可以向我展示如何取消并行的foreach。

我读到应该使用取消标记,但我找不到任何关于如何使用它的例子。

这是函数内部的原始for循环。

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;

这就是我使用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;

在 Parallel.ForEach 中,我无法执行 Result := 'ERROR',因为它不在该过程中捕获,所以我认为如果我可以取消 Parallel.ForEach 并报告该取消,则我可以在外部直接分配 Result := 'ERROR'

但是我对 OmniThreadLibrary 不熟悉,不知道如何做,请帮忙 :)

2个回答

0

你需要使用一个取消标记:

var
  cancelToken: IOmniCancellationToken;

您可以通过从OtlSync单元调用CreateOmniCancellationToken来获取取消令牌。

cancelToken := CreateOmniCancellationToken;

然后您将令牌提供给并行循环:

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

通过调用其 Signal 方法来发出取消令牌的信号。

cancelToken.Signal;

在并行循环之外,您可以使用

cancelToken.IsSignaled

可以检测到您已取消操作。或者,您可以从周围的作用域中捕获一个布尔变量,并通过该变量传递信息。

此处的示例提供了说明。


关于这个问题,我想提醒一下。如果你有一个OnStop事件,它将在正常完成和取消时都被调用。 - Graymatter
这将简单地终止并行迭代;他正在寻找一种报告错误条件的方法。 - Mason Wheeler
@Mason He已经在问题中描述了如何做到这一点。而且这个问题是要求如何终止for each循环。 - David Heffernan

0
取消标记只是其中的一半。如果你需要返回一个值,你需要使用Aggregate,因为序列中可以有任意数量的元素,但只能有一个返回值,所以你需要将任意数量的返回值合并(聚合)成一个最终值。所以你需要这样做:
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;

这可能不是完美的,但它应该能让你走上正确的轨道。


聚合在这里有些过头了。从包含作用域中捕获变量或直接使用标记本身:IsSignaled更容易。 - David Heffernan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接