Exceptions em Thread Paralelas – Estendendo TTask

Quando estamos executando um código em um processo paralelo (Thread) e ocorre uma Exception internamente, normalmente nada é apresentado ao usuário.

Isso ocorre por que a Thread em paralelo não consegue notificar a Thread Principal da aplicação para exibir a exceção pro usuário.

Vamos demonstrar um exemplo:

var
  FTask: ITask;
 
procedure TForm1.Button1Click(Sender: TObject);
begin
  ExecProc;
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  FTask.Cancel;
end;

procedure TForm1.ExecProc;
begin
  FTask := TTask.Create(
    procedure
    var
    I : Integer;
    begin
      for I := 0 to 9 do
      begin
        if TTask.CurrentTask.Status = TTaskStatus.Canceled then
          exit;
        Sleep(1000);
        if I = 2 then
          raise Exception.Create(‘Test exception’);
      end;
      if TTask.CurrentTask.Status <> TTaskStatus.Canceled then
      begin
        TThread.Queue(TThread.CurrentThread,
          procedure
          begin
            if Assigned(ListBox1) then
              Listbox1.Items.Add(’10 Seconds’);
          end);
      end;
    end);
  Task.Start;
end;

Executando o código é possível constatar que o procedimento levanta uma exceção e o usuário não recebe a informação de erro.

Stefan Glienke criou uma classe herdada de ITask e TTask com melhorias e mais simples de implementar para permitir tratar as exceções que ocorrem dentro da Thread.
Ele utilizou como modelo a Task.ContinueWith do .NET que permite continuar a execução após a ocorrência da exceção. Unit ThreadingEx:

Exemplo de como utilizar a nova implementação de TTask:

TTaskEx.Run(
  procedure
  begin
    Sleep(1000);
    raise Exception.Create(‘Test Error’)
  end)
  .ContinueWith(
    procedure(const LTaskEx: ITaskEx)
    begin
      TThread.Queue(TThread.CurrentThread,
      procedure
      begin
        ShowMessage(LTaskEx.ExceptObj.ToString);
      end);
    end
  , OnlyOnFaulted);

Melhorando o tratamento da exceção:

Se utilizarmos o código padrão:

O retorno da mensagem será: “One or more errors occurred”

Para resolver isso podemos utilziar:

E o retorno será:

One or more errors occurred

1 exceptions(s):

#0 Exception: Test Error

Isso ocorre pois podemos ter mais de um erro dentro da Exception.

Para melhorar podemos utilizar EAggregateException que contem as propriedades: Count e InnerExceptions[ ]

Exemplo 1:

for var I := 0 to Pred(EAggregateException(LTaskEx.ExceptObj).Count) do
  ShowMessage(EAggregateException(LTaskEx.ExceptObj).InnerExceptions[I].Message);

Exemplo 2:

Assim caso tenha mais de uma mensagem de exception poderemos exibi-las ao usuário.

Créditos: http://tireideletra.wbagestao.com/index.php/2016/03/24/exceptions-em-thread-paralelas-extendendo-ttask/

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

Rolar para cima