TGViewer
.NET Разработчик .NET Разработчик @netdeveloperdiary · 6.77K subscribers
Post #2094 1.97K
День 1724. #ЗаметкиНаПолях
Структурированный Параллелизм в C#
Структурированный параллелизм — это концепция, которая помогает писать более надёжный и удобный в обслуживании асинхронный код. Она не нова, но не так широко известна. Идея состоит в том, чтобы иметь возможность группировать асинхронные операции и гарантировать, что все операции завершатся до завершения группы. Это делается с помощью специальной конструкции, называемой областью задачи (Task Scope). Область задачи — это конструкция, похожая на блок try-catch-finally. Она используется для группировки асинхронных операций и обеспечения завершения всех операций до завершения группы.
Рассмотрим пример:
public class TaskScope
{
private CancellationTokenSource _cts = new();
private ConcurrentBag<Task> _tasks = new();

private TaskScope() { }

public static async Task Create(
Func<TaskScope, Task> act)
{
await using var ts = new TaskScope();
await act(ts);
await ts.WaitForAll();
}

public static async Task Create(
Action<TaskScope> act)
{
await using var ts = new TaskScope();
act(ts);
await ts.WaitForAll();
}

public async ValueTask DisposeAsync()
{
_cts.Cancel();
await WaitForAll();
}

public Task Run(
Func<CancellationToken, Task> act)
{
var task = Task.Run(async () =>
{
try
{
await act(_cts.Token);
}
catch (Exception ex) when (
ex is not OperationCanceledException)
{
_cts.Cancel();
throw;
}
});

_tasks.Add(task);
return task;
}

private async Task WaitForAll()
{
try
{
await Task.WhenAll(_tasks.ToArray());
}
catch (Exception ex) when (
ex is not OperationCanceledException)
{
throw;
}
}
}
Извините за много кода, но это только упрощённая реализация.
Идея в том, что мы определяем области в методе Create. Всё внутри Create будет выполняться в определённой области. Таким образом, задачи логически связаны друг с другом: если в одной выбрасывается исключение, мы отменяем все задачи вместе.
Использование:
var tasks = TaskScope.Create(group =>
{
group.Run(async token =>
{
await Task.Delay(100, token);
throw new Exception("Boom!!!");
});
group.Run(async token =>
await Task.Delay(1000, token));
});

// Выполняется 100мс
// после ошибки в 1й задаче отменяются все.
// Исключение поднимается наверх
try
{
await tasks;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}

// True
Console.WriteLine(tasks.IsFaulted);
Как видите, мы можем сгруппировать задачи и убедиться, что все они выполнены до того, как будет завершена область действия. Если одна из задач завершается сбоем, все остальные задачи отменяются и появляется исключение. Это очень мощная концепция, которая помогает писать более надёжный и удобный в обслуживании асинхронный код. Более сложную реализацию можно найти здесь.

Источник: https://steven-giesel.com/blogPost/59e57336-7c73-472f-a781-b0b79f0d47ad
  • 👍 25
More from @netdeveloperdiary
  1. Oct 10, 2026День 2810. #ЧтоНовенького #VSCode Более Быстрый и Лёгкий C# Dev Kit Мы, разработчики, люби…
  2. Oct 9, 2026День 2809. #Карьера 5 Навыков, Которые Помогут Быстрее Стать Сеньором. Окончание Начало 3.…
  3. Oct 8, 2026День 2808. #Карьера 5 Навыков, Которые Помогут Быстрее Стать Сеньором. Начало В ИТ есть се…
  4. Oct 7, 2026День 2807. #ЗаметкиНаПолях Типы Коллекций в .NET, Которые Стоит Попробовать. Окончание Нач…
  5. Oct 6, 2026🦈 Открытое собеседование на Middle C# | 6 октября, 19:00 МСК Приглашаем на открытое собес…
  6. Oct 6, 2026День 2806. #ЗаметкиНаПолях Типы Коллекций в .NET, Которые Стоит Попробовать. Начало Больши…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →