2015-08-18 3 views
7

У меня была проблема с улавливанием моего исключения из Task.Run. Я изменил свой код, и моя проблема была решена. Я хочу выяснить, в чем разница между обработкой исключений внутри Task.Run двумя способами:Как справиться с Task.Run Exception

Внешняя функция Я не могу поймать исключение, но внутри я могу его поймать.

void Outside() 
{ 
    try 
    { 
     Task.Run(() => 
     { 
      int z = 0; 
      int x = 1/z; 
     }); 
    } 
    catch (Exception exception) 
    { 
     MessageBox.Show("Outside : " + exception.Message); 
    } 
} 

void Inside() 
{ 
    Task.Run(() => 
    { 
     try 
     { 
      int z = 0; 
      int x = 1/z; 
     } 
     catch (Exception exception) 
     { 
      MessageBox.Show("Inside : "+exception.Message); 
     } 
    }); 
} 

EDIT (Дублированное Описание):

Я не хочу, чтобы мои Внутри и вне функций будут асинхронным, так как вызов await Task.Run(...) нуждаются в функции держателя, чтобы быть асинхронными.

+1

См. [Обработка исключений (параллельная библиотека задач)] (https://msdn.microsoft.com/en-us/library/dd997415%28v=vs.110%29.aspx) –

+2

Это не дубликат, если OP не использует 'await' ... И если он использует .net 4.x, он не может использовать' await'. –

+0

@MatthewWatson Вы уверены? 'await' - .NET 4.x! –

ответ

13

Когда задание выполняется, все исключения, которые он выбрасывает, сохраняются и повторно бросаются, когда что-то ждет результата задачи или завершения задачи.

Task.Run() возвращает Task объект, который можно использовать, чтобы сделать это, так:

var task = Task.Run(...) 

try 
{ 
    task.Wait(); // Rethrows any exception(s). 
    ... 

Для более новых версий C# вы можете использовать await вместо отъ Task.Wait():

try 
{ 
    await Task.Run(...); 
    ... 

который намного опережает.


Для полноты, вот компилируется консольное приложение, которое демонстрирует использование await:

using System; 
using System.Threading; 
using System.Threading.Tasks; 

namespace ConsoleApp1 
{ 
    class Program 
    { 
     static void Main() 
     { 
      test().Wait(); 
     } 

     static async Task test() 
     { 
      try 
      { 
       await Task.Run(() => throwsExceptionAfterOneSecond()); 
      } 

      catch (Exception e) 
      { 
       Console.WriteLine(e.Message); 
      } 
     } 

     static void throwsExceptionAfterOneSecond() 
     { 
      Thread.Sleep(1000); // Sleep is for illustration only. 
      throw new InvalidOperationException("Ooops"); 
     } 
    } 
} 
+1

Также' task.Result' выдает исключение, если оно есть. – VMAtm

+4

Даже с более новыми версиями C#, не всегда можно использовать 'await' вместо' Task.Wait() '. From [await (C# Reference)] (https://msdn.microsoft.com/en-us/library/hh156528%28v=vs.140%29.aspx): «Асинхронный метод, в котором ** ожидает ** используемый должен быть изменен с помощью ключевого слова [async] (https://msdn.microsoft.com/en-us/library/hh156513.aspx) ". – DavidRR

+0

var task = Task.Run (...) и после task.Wait() синхронизация вызова, конечно же, исключение, как и все операции синхронизации. второй вызов никогда не ожидает ожидания Task.Run (...) вызывает асинхронный вызов и снова вы исключаете исключение catch. или я испортил ... :) –

0

Для меня хотел, чтобы мой Task.Run продолжить после ошибки, позволяя сделку UI с как это имеет время.

Мое (странное?) Решение также должно иметь форму Form.Timer. У моей Task.Run есть очередь (для долговременных вещей, отличных от UI), а мой Form.Timer имеет свою очередь (для материала UI).

Поскольку этот метод уже работал на меня, было тривиально добавлять обработку ошибок: если task.Run получает сообщение об ошибке, он добавляет информацию об ошибке в очередь Form.Timer, которая отображает диалоговое окно с ошибкой.

3

Идея использования Task.Wait будет делать трюк, но вызовет вызывающий поток (по мере того, как говорит код) ждать и, следовательно, блокировать до завершения задачи, что фактически делает код синхронным, а не async.

Вместо этого используйте опцию Task.ContinueWith для достижения результатов:

Task.Run(() => 
{ 
    //do some work 
}).ContinueWith((t) => 
{ 
    if (t.IsFaulted) throw t.Exception; 
    if (t.IsCompleted) //optionally do some work); 
}); 

Если задача должна продолжать в потоке пользовательского интерфейса, нам вариант TaskScheduler.FromCurrentSynchronizationContext() в качестве параметра по-прежнему с примерно так:

).ContinueWith((t) => 
{ 
    if (t.IsFaulted) throw t.Exception; 
    if (t.IsCompleted) //optionally do some work); 
}, TaskScheduler.FromCurrentSynchronizationContext()); 

Этот код просто перестроит общее исключение из уровня задачи. Вы также можете ввести здесь какую-либо другую форму обработки исключений.

0

В своем внешнем коде вы проверяете, не запускает ли запуск задачи исключение, кроме самого тела задачи. Он выполняется асинхронно, и код, который его инициировал, выполняется тогда.

Вы можете использовать:

void Outside() 
{ 
    try 
    { 
     Task.Run(() => 
     { 
      int z = 0; 
      int x = 1/z; 
     }).GetAwaiter().GetResult(); 
    } 
    catch (Exception exception) 
    { 
     MessageBox.Show("Outside : " + exception.Message); 
    } 
} 

Использование .GetAwaiter().GetResult() ждет, пока задание не заканчивается и проходит брошено исключение, поскольку они и не завернуть их в AggregateException.