Асинхронная обработка исключений с помощью void - PullRequest
10 голосов
/ 08 июня 2011

Я использую Async CTP для написания тяжелого консольного приложения IO. Но у меня проблемы с исключениями.

public static void Main()
{
   while (true) {
     try{
         myobj.DoSomething(null);
     }
     catch(Exception){}
     Console.Write("done");
     //...
   }
}

//...
public async void DoSomething(string p)
{
   if (p==null) throw new InvalidOperationException();
   else await SomeAsyncMethod();
}

И происходит следующее: «Готово» записывается в консоль, затем я получаю исключение в отладчике, затем нажимаю «Продолжить» , моя программа существует .
Что дает?

Ответы [ 2 ]

17 голосов
/ 08 сентября 2011

Если вы предоставите вашему Консольному приложению асинхронно-совместимый контекст (например, AsyncContext ( документы , source ) из моей библиотеки AsyncEx), то вы можете перехватывать исключения, которые распространяются вне этого контекста, даже из async void методов:

public static void Main()
{
  try
  {
    AsyncContext.Run(() => myobj.DoSomething(null));
  }
  catch (Exception ex)
  {
    Console.Error.WriteLine(ex.Message);
  }
  Console.Write("done");
}

public async void DoSomething(string p)
{
  if (p==null) throw new InvalidOperationException();
  else await SomeAsyncMethod();
}
11 голосов
/ 09 июня 2011

Когда вы звоните DoSomething(), он в основном создает Task под капотом и начинает это Task. Так как у вас была подпись void, нет объекта Task, который мог бы дать сигнал назад или о котором вы могли бы заблокировать, поэтому выполнение сразу упало. Между тем задача выдает исключение, которое никто не ловит, поэтому я подозреваю, что ваша программа закрывается.

Я думаю, что поведение, которое вы хотели, больше похоже на это:

public static void Main()
{
   while (true) {
     var t = myobj.DoSomething(null);
     t.Wait();
     if(t.HasException) {
       break;
     }
   }
   Console.Write("done");
   //...
  }
}

//...
public async Task DoSomething(string p)
{
  if (p==null) throw new InvalidOperationException();
  else await SomeAsyncMethod();
}

Это будет блокировать каждый DoSomething до тех пор, пока это не будет сделано, и выйдет из цикла, если DoSomething скинул. Конечно, тогда вы действительно ничего не делаете асинхронно. Но из псевдокода я не могу точно сказать, что вы хотели, чтобы произошло асинхронно.

Основной вывод: использование void для асинхронного метода означает, что вы теряете возможность получить исключение, если вы не await используете этот асинхронный метод. Как синхронный вызов, он в основном просто планирует работу, и результат исчезает в эфире.

...