.NET Разработчик
6.54K subscribers
442 photos
3 videos
14 files
2.12K links
Дневник сертифицированного .NET разработчика. Заметки, советы, новости из мира .NET и C#.

Для связи: @SBenzenko

Поддержать канал:
- https://boosty.to/netdeveloperdiary
- https://patreon.com/user?u=52551826
- https://pay.cloudtips.ru/p/70df3b3b
Download Telegram
День 1413. #ЗаметкиНаПолях #AsyncTips
Выполнение Кода с Помощью Планировщика. Окончание: Использование Планировщика

Задача:
требуется управлять выполнением отдельных фрагментов в параллельном коде.

Решение
После того как вы создадите экземпляр TaskScheduler (см. Создание Планировщика), можете включить его в набор параметров, передаваемых методу Parallel. Следующий код получает набор коллекций матриц, запускает несколько параллельных циклов и ограничивает общий параллелизм всех циклов одновременно независимо от количества матриц в каждом наборе:
void RotateMatrices(
IEnumerable<IEnumerable<Matrix>> collections,
float degrees)
{
var schPair = new ConcurrentExclusiveSchedulerPair(
TaskScheduler.Default,
maxConcurrencyLevel: 8);
var scheduler = schPair.ConcurrentScheduler;
var opts = new ParallelOptions {
TaskScheduler = scheduler };

Parallel.ForEach(
collections,
opts,
matrices => Parallel.ForEach(
matrices,
opts,
m => m.Rotate(degrees)));
}

Parallel.Invoke также получает экземпляр ParallelOptions, поэтому вы можете передать TaskScheduler при вызове Parallel.Invoke так же, как и для Parallel.ForEach. При выполнении динамического параллельного кода можно передать TaskScheduler непосредственно TaskFactory.StartNew или Task.ContinueWith.

Передать TaskScheduler коду Parallel LINQ (PLINQ) невозможно.

Источник: Стивен Клири “Конкурентность в C#”. 2-е межд. изд. — СПб.: Питер, 2020. Глава 13.
👍3
День 2010. #ЗаметкиНаПолях #AsyncTips
Конечный Автомат в C# для async/await. Начало

Часто говорят, что ключевые слова async/await приводят к созданию конечного автомата. Но что это значит? Рассмотрим на простом примере:
async Task<Dto> GetAsync()
{
using var hc = new HttpClient();
hc.BaseAddress = new Uri("…");
var resp = await hc.GetAsync("");
resp.EnsureSuccessStatusCode();
var content =
await resp.Content.ReadAsStringAsync();
return JsonSerializer.Deserialize<Dto>(content);
}

public class Dto;

Здесь несколько вызовов await: для получения ответа, и для чтения содержимого.

Деление метода по границе await
Каждый раз при вызове await, мы знаем, что нам не нужно ничего делать, кроме как ждать результата. Логично при этом просто выйти из метода и вернуться, как только будет получен результат. Компилятор разделит метод по границе await и создаст конечный автомат. Вот упрощённый код:
public class GetAllAsync_StateMachine
{
public ContinuationMachine _builder =
ContinuationMachineBuilder.Create();

private int _state = 0;
private HttpClient _hc;
private HttpResponseMessage _resp;
private string _content;

private void MoveNext()
{
switch (_state)
{
case 0:
_hc = new HttpClient();
_hc.BaseAddress = new Uri("…");
_hc.GetAsync("");
_state = 1;
_builder.Continue(ref this);
break;
case 1:
_resp.EnsureSuccessStatusCode();
_resp.Content.ReadAsStringAsync();
_state = 2;
_builder.Continue(ref this);
break;
case 2:
return JsonSerializer.Deserialize<Dto>(_content);
}
}
}

Это очень упрощенная версия того, что делает компилятор, и она не учитывает важные части, например, как содержимое извлекается из HttpClient. Важно то, что мы синхронно вызываем часть метода до await, а затем используем механизм обратных вызовов (именно поэтому используется ref this) для продолжений метода. Таким образом, как только HTTP-вызов завершается, мы возвращаемся в метод и продолжаем с того места, где остановились (state = 1), когда завершается чтение – продолжаем со state=2.

Окончание следует…

Источник:
https://steven-giesel.com/blogPost/720a48fd-0abe-4c32-83ac-26926d501895/the-state-machine-in-c-with-asyncawait
👍21
День 2011. #ЗаметкиНаПолях #AsyncTips
Конечный Автомат в C# для async/await. Окончание

Начало

Планировщик заданий
Продолжения (обратные вызовы) размещаются в планировщике заданий (TaskScheduler). Он берёт конечный автомат и планирует его выполнение после завершения ожидаемой задачи. Таким образом, TaskScheduler отвечает за продолжения. Здесь есть ещё несколько интересных моментов, таких как контекст синхронизации (SynchronizationContext) и пул потоков (ThreadPool), но это детали.

Итак: при использовании async/await компилятор разделит метод по границам await и создаст конечный автомат. Этот конечный автомат будет запланирован в TaskScheduler для продолжения после завершения ожидаемой задачи.

Где хранится конечный автомат?
В общем случае - в Task или Task<T>. Там хранится текущее состояние (включая продолжения), а также результат ожидаемой задачи. Но, кроме того, объект Task также хранит исключения. Исключения в с async/await-коде немного отличаются от исключений в синхронном коде.

Рассмотрим следующий код:
static async Task ThrowExceptionAsync()
{
await Task.Yield();
throw new Exception("Ex");
}

Компилятор преобразует этот код в следующий:
try
{
YieldAwaitable.YieldAwaiter awaiter;
// Другой код
awaiter.GetResult();
throw new Exception("Ex");
}
catch (Exception exception)
{
<>1__state = -2;
<>t__builder.SetException(exception);
}

Важно отметить, что внутри блока catch нет throw. Т.е., если возникнет исключение в асинхронной части метода, оно не будет выброшено, а будет сохранено в объекте Task.

Возможно, теперь вы понимаете, почему async void — плохая идея: исключения будут потеряны. Они будут возникать, но вы не сможете их перехватить или обработать каким-либо образом. То же самое относится к async Task, если вы не ожидаете её. Поэтому:
static async Task ThrowExAsync()
{
throw new Exception("Ex");
await SomethingAsync();
}

// не выбросит исключения
_ = ThrowExAsync();

// не выбросит исключения
ThrowExAsync();

// выбросит исключение
await ThrowExAsync();

Исключение перехватывается и сохраняется в объекте Task, не выбрасываясь во внешний код. Но когда вы ожидаете объект Task, вызов await будет преобразован во что-то вроде GetAwaiter().GetResult(), и именно здесь исключение выбрасывается из объекта Task наружу.

Источник: https://steven-giesel.com/blogPost/720a48fd-0abe-4c32-83ac-26926d501895/the-state-machine-in-c-with-asyncawait
👍16👎1