2013-05-27 3 views
0

Вот мой код:Ловля исключения асинхронного в основном потоке

using System; 
using System.Data.SqlClient; 
using System.Threading; 
using System.Threading.Tasks; 

namespace TestAsync 
{ 
    class Program 
    { 
     private const string conn = "Data Source=UNREACHABLESERVER;Initial Catalog=master;Integrated Security=True"; 

     static void Main(string[] args) 
     { 
      try 
      { 
       TestConnection(); 
      } 
      catch 
      { 
       Console.WriteLine("Caught in main"); 
      } 
     } 

     private static async void TestConnection() 
     { 
      bool connected = false; 

      using (var tokenSource = new CancellationTokenSource()) 
      using (var connection = new SqlConnection(conn)) 
      { 
       tokenSource.CancelAfter(2000); 

       try 
       { 
        await connection.OpenAsync(tokenSource.Token); 
        connected = true; 
       } 
       catch(TaskCanceledException) 
       { 
        Console.WriteLine("Caught timeout"); 
       } 
       catch 
       { 
        Console.Write("Caught in function"); 
       } 

       if (connected) 
       { 
        Console.WriteLine("Connected!"); 
       } 
       else 
       { 
        Console.WriteLine("Failed to connect..."); 
        throw(new Exception("hi")); 
       } 
      } 
     } 
    } 
} 

Выход:

Caught timeout 
Failed to connect... 

Но тогда моя программа завершается с необработанным исключением. Вместо этого я хочу, чтобы моя программа имела исключенное исключение в основном потоке и распечатывала Caught in main. Как я могу сделать эту работу?

EDIT

Вот мой обновленный код, который работает так, как я хочу:

using System; 
using System.Data.SqlClient; 
using System.Threading; 
using System.Threading.Tasks; 

namespace TestAsync 
{ 
    class Program 
    { 
     private const string conn = "Data Source=UNREACHABLESERVER;Initial Catalog=MyFiles;Integrated Security=True"; 

     static void Main(string[] args) 
     { 
      try 
      { 
       TestConnection().Wait(); 
      } 
      catch 
      { 
       Console.WriteLine("Caught in main"); 
      } 

      Console.ReadLine(); 
     } 

     private static async Task TestConnection() 
     { 
      using (var tokenSource = new CancellationTokenSource()) 
      using (var connection = new SqlConnection(conn)) 
      { 
       tokenSource.CancelAfter(2000); 
       await connection.OpenAsync(tokenSource.Token); 
      } 
     } 
    } 
} 

ответ

4

Это не представляется возможным. Ваш звонок на TestConnection() будет возвращен (и, следовательно, выполнение по основному потоку будет продолжаться), как только ваш первый await встретится. Ваши блоки catch и бросание исключения будут выполняться в другом потоке и не будут отслеживаться основным потоком.

Это одна из причин, почему следует избегать async void. Если вам нужно написать функцию async void, то она должна быть полностью автономной (включая логику обработки ошибок) должна. Вам было бы намного лучше написать функцию async Task. Самый простой подход должен был бы изменить свой вызов в методе Main к этому:

TestConnection().Wait() 

Это, конечно, вызывает основной поток, чтобы блокировать во время выполнения функции (вы также должны изменить подпись async Task, прежде чем это скомпилируется).

+2

Исправить, за исключением второго предложения. Методы 'async' всегда запускаются синхронно. Однако, даже если исключение выбрасывается во время синхронной части, оно не будет передаваться вызывающему. –

+0

@ StephenCleary: Верно, не уверен, о чем я думал. Отредактировано, чтобы это отразить. –

+0

Я чувствую, что ваше редактирование не удовлетворило проблему @ Стивена. В частности, ваш ответ по-прежнему подразумевает, что исключение _allways_ происходит в другом потоке. Это не так. В частности, даже если «ожидание» завершается синхронно, так что продолжение выполняется в текущем потоке, исключение не будет наблюдаться вызывающим. Это по семантике дизайна метода async. –

Смежные вопросы