在调用TTask.WaitForAny之后获取失败任务的索引

时间:2015-09-26 09:14:20

标签: multithreading delphi

考虑以下示例:

 t1 := TTask.Run(
    procedure
    begin
      sleep(Random(1000) + 100);
      raise Exception.Create('Error Message 1');
    end
  );

  t2 := TTask.Run(
    procedure
    begin
      sleep(Random(1000) + 100);
      raise Exception.Create('Error Message 2');
    end
  );

  res := -1;
  try
    res:= TTask.WaitForAny([t1, t2]);
  except
  end;

  write('Index: ' + intToStr(res)); // Prints "-1"
  readln;

当其中一个任务失败时,WaitForAny会抛出异常,并且res变量未分配已完成任务的索引。相反,它包含" -1" (早先指定)。

在C#(TPL)Task.WaitAny中也会抛出异常,但它会返回失败任务的索引。

有没有办法分配RES变量或获取失败任务的索引?

1 个答案:

答案 0 :(得分:0)

您写道:

  

在C#(TPL)Task.WaitAny中也会抛出异常,但它会返回失败任务的索引。

那不是真的。考虑以下C#程序:

using System;
using System.Threading.Tasks;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            Task t1 = Task.Run(() => { throw new Exception("task 1"); });
            Task t2 = Task.Run(() => { throw new Exception("task 2"); });
            int index = Task.WaitAny(new Task[] { t1, t2 });
            Console.WriteLine(index);
            Console.ReadLine();
        }
    }
}

两个任务都抛出异常,但WaitAny没有。它返回首先完成的任务的索引值,即使该任务引发了异常。

相比之下,如果要完成的第一个任务是通过引发异常,则Delphi TTask.WaitForAny方法将引发异常。在这种情况下,由于函数引发异常时未分配函数返回值,因此您需要找到一种不同的方法来标识导致WaitForAny返回的任务。例如,这是一种相当蹩脚的方法:

{$APPTYPE CONSOLE}

uses
  System.SysUtils, System.Threading;

type
  ETaskException = class(Exception)
  private
    FTaskIndex: Integer;
  public
    constructor Create(const Msg: string; const TaskIndex: Integer);
    property TaskIndex: Integer read FTaskIndex;
  end;

constructor ETaskException.Create(const Msg: string; const TaskIndex: Integer);
begin
  inherited Create(Msg);
  FTaskIndex := TaskIndex;
end;

var
  t1, t2: ITask;
  i, res: Integer;

begin
 t1 := TTask.Run(
    procedure
    begin
      sleep(150);
      raise ETaskException.Create('Task failed', 0);
    end
  );

  t2 := TTask.Run(
    procedure
    begin
      sleep(100);
      raise ETaskException.Create('Task failed', 1);
    end
  );

  try
    res := TTask.WaitForAny([t1, t2]);
    Writeln('Task succeeded, index ' + IntToStr(res));
  except
    on E:EAggregateException do begin
      for i := 0 to E.Count-1 do begin
        WriteLn('Task failed, index ' + 
          IntToStr((E.InnerExceptions[i] as ETaskException).TaskIndex));
      end;
    end;
  end;

  ReadLn;
end.

虽然我已经在这里讨论了聚合异常,但我相信WaitForAny只会有一个内部异常。

您可能最好只循环查找Status等于TTaskStatus.Exception的任务。