Полное руководство по языку программирования С# 14 и платформе .NET 10 / Parallel LINQ

Обработка ошибок и отмена операции

Начальный ⏱ 2 мин чтения 👁 2 🕓 обновлено 11.08.2026

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

При генерации исключений все они агрегируются в одном исключении типа AggregateException

Например, пусть в метод факториала передается массив объектов, который содержит не только числа, но и строки:


object[] numbers = new object[] { 1, 2, 3, 4, 5, "6" };

var squares = from n in numbers.AsParallel()
                 let x = (int)n
                 select Square(x);
try
{
    squares.ForAll(n => Console.WriteLine(n));
}
catch (AggregateException ex)
{
    foreach (var e in ex.InnerExceptions)
    {
        Console.WriteLine(e.Message);
    }
}

int Square(int n) => n * n;

Запустим проект без отладки. И так как массив содержит строку, то попытка приведения закончится неудачей, и на консоль будет выведено сообщение об ошибке. При запуске приложения в Visual Studio в режиме отладки выполнение остановится на строке преобразования. А после продолжения также сработает перехват исключения в блоке catch, и на консоль будет выведено сообщение об ошибке.

Прерывание параллельной операции

Вполне вероятно, что нам может потребоваться прекратить операцию до ее завершения. В этом случае мы можем использовать метод WithCancellation(), которому в качестве параметра передается токен CancellationToken:


CancellationTokenSource cts = new CancellationTokenSource();
// запускаем дополнительную задачу, в которой через 400 миллисек. прерываем операцию
new Task(() =>
{
    Thread.Sleep(400);
    cts.Cancel();
}).Start();

try
{
    int[] numbers = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, };

    var squares = from n in numbers.AsParallel().WithCancellation(cts.Token)
                     select Square(n);

    foreach (var n in squares)
        Console.WriteLine(n);
}
catch (OperationCanceledException)
{
    Console.WriteLine("Операция была прервана");
}
catch (AggregateException ex)
{
    if (ex.InnerExceptions != null)
    {
        foreach (Exception e in ex.InnerExceptions)
            Console.WriteLine(e.Message);
    }
}
finally
{
    cts.Dispose();
}
int Square(int n)
{
    var result = n * n;
    Console.WriteLine($"Квадрат числа {n} равен {result}");
    Thread.Sleep(1000); // имитация долгого вычисления
    return result;
}

В параллельной запущенной задаче вызывается метод cts.Cancel(), что приводит к завершению операции и генерации исключения OperationCanceledException:

Квадрат числа 5 равен 25
Квадрат числа 3 равен 9
Квадрат числа 1 равен 1
Квадрат числа 7 равен 49
Операция была прервана

При этом также имеет смысл обрабатывать исключение AggregateException, так как если параллельно возникает еще одно исключение, то это исключение, а также OperationCanceledException помещаются внутрь одного объекта AggregateException.

Проверь себя

📋 Тест по теме «Обработка ошибок и отмена операции» Тест по теме

Практические задания

Лёгкое Ручная проверка Заполните пропуск в коде (1)
+10 XP

В приведённом ниже фрагменте кода из этого урока пропущено одно слово. Определите его и впишите в поле ответа (без кавычек и пробелов).

_____[] numbers = new object[] { 1, 2, 3, 4, 5, "6" };

var squares = from n in numbers.AsParallel()
                 let x = (int)n
                 select Square(x);
try
{
    squares.ForAll(n => Console.WriteLine(n));
}
catch (AggregateException ex)
{
    foreach (var e in ex.InnerExceptions)
    {
        Console.WriteLine(e.Message);
    }
}

int Square(int n) => n * n;
Войдите, чтобы отправить решение
Лёгкое Ручная проверка Заполните пропуск в коде (2)
+10 XP

В приведённом ниже фрагменте кода из этого урока пропущено одно слово. Определите его и впишите в поле ответа (без кавычек и пробелов).

CancellationTokenSource _____ = new CancellationTokenSource();
// запускаем дополнительную задачу, в которой через 400 миллисек. прерываем операцию
new Task(() =>
{
    Thread.Sleep(400);
    cts.Cancel();
}).Start();

try
{
    int[] numbers = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, };

    var squares = from n in numbers.AsParallel().WithCancellation(cts.Token)
                     select Square(n);

    foreach (var n in squares)
        Console.WriteLine(n);
}
catch (OperationCanceledException)
{
    Console.WriteLine("Операция была прервана");
}
catch (AggregateException ex)
{
    if (ex.InnerExceptions != null)
    {
        foreach (Exception e in ex.InnerExceptions)
            Console.WriteLine(e.Message);
    }
}
finally
{
    cts.Dispose();
}
int Square(int n)
{
    var result = n * n;
    Console.WriteLine($"Квадрат числа {n} равен {result}");
    Thread.Sleep(1000); // имитация долгого вычисления
    return result;
}
Войдите, чтобы отправить решение
Лёгкое Ручная проверка Заполните пропуск в коде (3)
+10 XP

В приведённом ниже фрагменте кода из этого урока пропущено одно слово. Определите его и впишите в поле ответа (без кавычек и пробелов).

object[] numbers = new object[] { 1, _____, 3, 4, 5, "6" };

var squares = from n in numbers.AsParallel()
                 let x = (int)n
                 select Square(x);
try
{
    squares.ForAll(n => Console.WriteLine(n));
}
catch (AggregateException ex)
{
    foreach (var e in ex.InnerExceptions)
    {
        Console.WriteLine(e.Message);
    }
}

int Square(int n) => n * n;
Войдите, чтобы отправить решение
Лёгкое Ручная проверка Заполните пропуск в коде (4)
+10 XP

В приведённом ниже фрагменте кода из этого урока пропущено одно слово. Определите его и впишите в поле ответа (без кавычек и пробелов).

CancellationTokenSource cts = new CancellationTokenSource();
// запускаем дополнительную задачу, в которой через 400 миллисек. прерываем операцию
new _____(() =>
{
    Thread.Sleep(400);
    cts.Cancel();
}).Start();

try
{
    int[] numbers = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, };

    var squares = from n in numbers.AsParallel().WithCancellation(cts.Token)
                     select Square(n);

    foreach (var n in squares)
        Console.WriteLine(n);
}
catch (OperationCanceledException)
{
    Console.WriteLine("Операция была прервана");
}
catch (AggregateException ex)
{
    if (ex.InnerExceptions != null)
    {
        foreach (Exception e in ex.InnerExceptions)
            Console.WriteLine(e.Message);
    }
}
finally
{
    cts.Dispose();
}
int Square(int n)
{
    var result = n * n;
    Console.WriteLine($"Квадрат числа {n} равен {result}");
    Thread.Sleep(1000); // имитация долгого вычисления
    return result;
}
Войдите, чтобы отправить решение
Лёгкое Ручная проверка Заполните пропуск в коде (5)
+10 XP

В приведённом ниже фрагменте кода из этого урока пропущено одно слово. Определите его и впишите в поле ответа (без кавычек и пробелов).

object[] numbers = new object[] { 1, 2, 3, 4, 5, "6" };

var _____ = from n in numbers.AsParallel()
                 let x = (int)n
                 select Square(x);
try
{
    squares.ForAll(n => Console.WriteLine(n));
}
catch (AggregateException ex)
{
    foreach (var e in ex.InnerExceptions)
    {
        Console.WriteLine(e.Message);
    }
}

int Square(int n) => n * n;
Войдите, чтобы отправить решение
Среднее Ручная проверка Восстановите код по памяти (1)
+20 XP

В этом фрагменте кода урока пропущены два ключевых элемента. Впишите оба пропущенных значения через запятую в порядке появления (например: x, 5).

object[] numbers = new object[] { 1, 2, 3, 4, 5, "6" };

var _____ = from n in numbers.AsParallel()
                 let x = (int)n
                 select Square(x);
try
{
    _____.ForAll(n => Console.WriteLine(n));
}
catch (AggregateException ex)
{
    foreach (var e in ex.InnerExceptions)
    {
        Console.WriteLine(e.Message);
    }
}

int Square(int n) => n * n;
Войдите, чтобы отправить решение
Среднее Ручная проверка Восстановите код по памяти (2)
+20 XP

В этом фрагменте кода урока пропущены два ключевых элемента. Впишите оба пропущенных значения через запятую в порядке появления (например: x, 5).

CancellationTokenSource cts = _____ CancellationTokenSource();
// запускаем дополнительную задачу, в которой через 400 миллисек. прерываем операцию
new Task(() =>
{
    Thread.Sleep(400);
    cts.Cancel();
}).Start();

try
{
    int[] numbers = new int[] { 1, 2, 3, 4, 5, 6, _____, 8, };

    var squares = from n in numbers.AsParallel().WithCancellation(cts.Token)
                     select Square(n);

    foreach (var n in squares)
        Console.WriteLine(n);
}
catch (OperationCanceledException)
{
    Console.WriteLine("Операция была прервана");
}
catch (AggregateException ex)
{
    if (ex.InnerExceptions != null)
    {
        foreach (Exception e in ex.InnerExceptions)
            Console.WriteLine(e.Message);
    }
}
finally
{
    cts.Dispose();
}
int Square(int n)
{
    var result = n * n;
    Console.WriteLine($"Квадрат числа {n} равен {result}");
    Thread.Sleep(1000); // имитация долгого вычисления
    return result;
}
Войдите, чтобы отправить решение
Сложное Ручная проверка Объясните и модифицируйте пример из урока «Обработка ошибок и отмена операции»
+30 XP

Опишите своими словами, что делает приведённый пример кода из урока «Обработка ошибок и отмена операции», и предложите одно осмысленное изменение (например, другое входное значение или дополнительное условие). Приведите итоговый код.

object[] numbers = new object[] { 1, 2, 3, 4, 5, "6" };

var squares = from n in numbers.AsParallel()
                 let x = (int)n
                 select Square(x);
try
{
    squares.ForAll(n => Console.WriteLine(n));
}
catch (AggregateException ex)
{
    foreach (var e in ex.InnerExceptions)
    {
        Console.WriteLine(e.Message);
    }
}

int Square(int n) => n * n;
Войдите, чтобы отправить решение
Сложное Ручная проверка Найдите потенциальную проблему в коде урока «Обработка ошибок и отмена операции»
+30 XP

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

CancellationTokenSource cts = new CancellationTokenSource();
// запускаем дополнительную задачу, в которой через 400 миллисек. прерываем операцию
new Task(() =>
{
    Thread.Sleep(400);
    cts.Cancel();
}).Start();

try
{
    int[] numbers = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, };

    var squares = from n in numbers.AsParallel().WithCancellation(cts.Token)
                     select Square(n);

    foreach (var n in squares)
        Console.WriteLine(n);
}
catch (OperationCanceledException)
{
    Console.WriteLine("Операция была прервана");
}
catch (AggregateException ex)
{
    if (ex.InnerExceptions != null)
    {
        foreach (Exception e in ex.InnerExceptions)
            Console.WriteLine(e.Message);
    }
}
finally
{
    cts.Dispose();
}
int Square(int n)
{
    var result = n * n;
    Console.WriteLine($"Квадрат числа {n} равен {result}");
    Thread.Sleep(1000); // имитация долгого вычисления
    return result;
}
Войдите, чтобы отправить решение
Мини-проект Ручная проверка Мини-проект по теме «Обработка ошибок и отмена операции»
+50 XP

Соберите небольшую программу, которая последовательно демонстрирует ключевые идеи урока. Программа должна быть рабочей и содержать минимум два самостоятельно написанных фрагмента, а не просто копию примера из урока.

Войдите, чтобы отправить решение
Домашнее задание Ручная проверка Домашнее задание по теме «Обработка ошибок и отмена операции»
+25 XP

Напишите собственную небольшую программу, применяющую материал темы «Обработка ошибок и отмена операции». Используйте не менее одной конструкции, показанной в примерах урока. Прикрепите код решения и краткое описание того, что он делает.

Войдите, чтобы отправить решение

Обсуждение (0)

Войдите, чтобы оставить комментарий.