Асинхронні методи void та обробка помилок

Зазвичай асинхронні методи C# мають значення, яке представляє відповідну задачу, наприклад, Task або Task.

Якщо під час виконання асинхронного методу виникає необроблена помилка, вона передається назад у місце, де очікується ця задача.

async Task Foo()  
{  
 await Task.Sleep(1);  
 throw new Exception();  
}  

async Task Bar()  
{  
 var task = Foo();  

 try   
 {  
 await task;  
 }  
 catch  
 {  
 // Помилку, що виникла в Foo, можна обробити тут.  
 }  
}

А що буде, якщо не дочекатися завершення задачі? Помилка залишиться необробленою, і головний потік буде продовжувати виконання без змін. Нічого не станеться.

Однак ситуація змінюється, якщо асинхронний метод не має повернутого значення. Оскільки помилка не збирається в Task або подібному, вона передається назад у головний потік і призводить до зупинки всієї програми.

async void Foo()  
{  
 await Task.Sleep(1);  
 throw new Exception(); // Програма зупиняється на цьому місці.  
}  

void Bar()  
{  
 try  
 {  
 Foo();  
 }  
 catch  
 {  
 // Тут помилка не буде зібрана.  
 }  
}

Це стає особливо проблематичним, коли метод викликається ззовні програми.

Припустимо, що метод Foo з прикладу вище є частиною окремої бібліотеки. Автор методу Bar може не знати, що Foo — асинхронний метод, і спробує обробити помилку, як у наведеному коді.

В результаті помилка не буде оброблена в блоці catch, і програма зупиниться. Якщо не знати про таку особливість, пошук причини проблеми може зайняти досить багато часу.

Перекладено з: Async void와 예외 처리

Leave a Reply

Your email address will not be published. Required fields are marked *