JavaRush /Курсы /C# SELF /Обработка ошибок в асинхронных операциях (

Обработка ошибок в асинхронных операциях ( async/ await)

C# SELF
42 уровень , 3 лекция
Открыта

1. Введение

Начнем с важного вопроса: почему нельзя просто довериться асинхронным методам и надеяться, что всё и всегда сработает отлично? Операции с файлами нередко бросают исключения — файл могли удалить, закончилась память, не хватает прав или файл заблокирован. В синхронном коде вы бы ловили проблемы в привычном try-catch-блоке. В асинхронном коде философия та же, но есть нюансы: ошибка может произойти не сразу при вызове метода, а позже, когда операция реально выполняется.

Момент возникновения ошибки

В синхронном коде при чтении через StreamReader.Read() исключение вылетит прямо в строке вызова — поймали в catch, и хорошо.
В асинхронном коде (await stream.ReadAsync()) ошибка упадёт не в момент старта операции, а непосредственно при await — когда задача завершится с ошибкой. Если забыть поставить await, ошибка может остаться «невидимой» до поры до времени.

2. Как ловить исключения в асинхронных методах

Давайте сразу посмотрим типовой шаблон:


try
{
    using FileStream fs = new FileStream("myfile.txt", FileMode.Open);
    byte[] buffer = new byte[1024];
    int bytesRead = await fs.ReadAsync(buffer, 0, buffer.Length);
    // Дальнейшая обработка...
}
catch (IOException ex)
{
    Console.WriteLine("Ошибка ввода-вывода: " + ex.Message);
}
catch (UnauthorizedAccessException ex)
{
    Console.WriteLine("Нет доступа к файлу: " + ex.Message);
}
catch (Exception ex)
{
    Console.WriteLine("Неизвестная ошибка: " + ex);
}

Да-да, всё просто — используем знакомый try-catch, но в асинхронном методе. Важно, чтобы сам метод был с ключевым словом async, иначе компилятор рассердится.

Важный нюанс: где ставить await


Task<int> readTask = fs.ReadAsync(buffer, 0, buffer.Length);
// ... тут случайно забыли await или обработку

В таком случае ошибка, если она случится, попадёт в саму задачу (Task), и вы о ней не узнаете, пока не попробуете получить результат — например, через await readTask или через свойство Task.Exception. Если вообще забыли про await — задача может завершиться с ошибкой, а вам об этом никто не скажет.

3. Почему без обработки ошибок асинхронный код коварен

Сценарий 1: «Fire and forget» — ловушка новичка


FileStream fs = new FileStream("file.txt", FileMode.Open);
byte[] buffer = new byte[8000];
fs.ReadAsync(buffer, 0, buffer.Length);
// А дальше программа живет себе своей жизнью

Операция чтения уходит в «параллельное плавание», и если она завершится с ошибкой, никакой catch её не поймает. Исключение спрячется внутри задачи. Этот паттерн называют «fire and forget» и в реальных приложениях он чреват потерей критических ошибок и утечками ресурсов.

Сценарий 2: Асинхронные методы без await


Task t = MyAsyncMethod();
// ... тут что-то делаем, а потом забыли про t

Ошибки, произошедшие внутри MyAsyncMethod, не поднимутся, пока вы явно не дождётесь задачи (await t или t.Wait()).

4. Как правильно ловить ошибки от асинхронных задач

Стратегия 1: Всегда используйте await


try
{
    await SomeFileOperationAsync();
}
catch (Exception ex)
{
    Console.WriteLine("Что-то пошло не так: " + ex.Message);
}

Так исключение будет выброшено прямо в месте ожидания и не потеряется.

Стратегия 2: Обработка с помощью .ContinueWith

Если по какой-то причине вы не используете await, можно добавить обработчик ошибок через ContinueWith:


var task = fs.ReadAsync(buffer, 0, buffer.Length);
task.ContinueWith(t =>
{
    if (t.Exception != null)
        Console.WriteLine("Ошибка при асинхронном чтении: " + t.Exception.InnerException);
}, TaskContinuationOptions.OnlyOnFaulted);

Честно? В современных приложениях C# так делают редко — async/await делает код проще и чище.

Возможные исключения при работе с файлами асинхронно

  • IOException — сбой диска, файл не найден, слишком длинный путь, устройство недоступно.
  • UnauthorizedAccessException — недостаточно прав.
  • ObjectDisposedException — поток был закрыт до окончания операции.
  • OperationCanceledException — операция отменена через токен отмены (CancellationToken).

5. Пример: Асинхронное чтение с обработкой ошибок

Добавим эту логику в наше приложение:


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

namespace FileAsyncDemo
{
    class Program
    {
        static async Task Main()
        {
            string path = "bigfile.txt";
            byte[] buffer = new byte[4096];

            try
            {
                using FileStream fs = new FileStream(path, FileMode.Open);
                int bytesRead = await fs.ReadAsync(buffer, 0, buffer.Length);
                Console.WriteLine($"Прочитано {bytesRead} байт из {path}");
            }
            catch (FileNotFoundException ex)
            {
                Console.WriteLine("Файл не найден: " + ex.Message);
            }
            catch (UnauthorizedAccessException ex)
            {
                Console.WriteLine("Нет доступа к файлу: " + ex.Message);
            }
            catch (IOException ex)
            {
                Console.WriteLine("Ошибка чтения/записи: " + ex.Message);
            }
            catch (Exception ex)
            {
                Console.WriteLine("Другая ошибка: " + ex.Message);
            }
        }
    }
}

Важное замечание

Если вы не используете await в Main, а делаете только Task result = SomeAsyncMethod(); — ошибки будут «молчать» и проявятся позже, когда вы всё-таки попытаетесь получить результат.

6. Самые частые ошибки и «грабли» при обработке ошибок

Забывают ставить await на асинхронный метод — ошибки не всплывают вовремя, программа ведёт себя непредсказуемо.

Не оборачивают асинхронные вызовы в try-catch — приложение падает при первом же сбое.

Обрабатывают только Exception, игнорируя специфичные исключения вроде UnauthorizedAccessException или OperationCanceledException — в итоге плохая диагностика.

Используют «fire and forget» задачи без явного логирования ошибок — исключения теряются внутри Task.

Считают, что если задача завершилась — значит всё успешно. Нужно явно ждать результат (await) и обрабатывать исключения.

2
Задача
C# SELF, 42 уровень, 3 лекция
Недоступна
Обработка ошибки при чтении файла
Обработка ошибки при чтении файла
Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ