Есть несколько недоразумений в том, как вы пытаетесь использовать Async-Await
, в сети достаточно контента, ниже приведены конкретные детали:
Так что здесь, когда я запускаю программу и отлаживаю ее, курсор сначала идет в метод ExecuteAsync, а из цикла while он идет в метод перенаправления. Метод перенаправления асинхронный, но он будет ждать метода работы. так что я думаю, что он не работает асинхронно
Что делает await
, это освобождает вызывающий контекст, в то время как обработка идет в фоновом режиме, что помогает системе оставаться отзывчивой, таким образом, система имеет масштабируемость / пул потоков для обслуживания дальнейших запросов, поэтому единственное, что await
не делает do блокирует вызывающий поток / контекст, в отладчике, как видно из вашего кода, он будет ждать завершения.
Теперь, какая обработка work
делает это IO или Compute
Если это IO
, то здесь действительно используется асинхронная обработка на сервере, поскольку поток пула потоков освобождается, и при использовании вместе с ConfigureAwait(false)
нет необходимости повторно вводить тот же контекст для обработки ответа, что ожидается по библиотекам. Если он вычисляется, то он хорошо работает для пользовательского интерфейса, такого как WPF, поскольку поток пользовательского интерфейса остается отзывчивым, но обработка по-прежнему отбирает потоки из текущего пула, а доступность ограничена / ограничена.
Относительно вашего кода
- В текущей форме это код блокировки, так как в цикле while вы вызываете
redirect(work)
, который, в свою очередь, выполняет await work
, освобождает вызывающую программу, но ожидает его завершения, прежде чем может быть выполнена следующая асинхронная операция
- Идеальный способ - собрать все задачи в коллекции, например,
List<Task>
, и когда вы ожидаете Task.WhenAll
, тогда все объединенные задачи объединяются, в то время как вы асинхронно ждете у единственного представителя Task
, чтобы все они выполнились. , это все равно не blcoking / call, как в текущем коде, а блокировка для полного агрегированного списка, это определяется @TheGeneral в комментариях, таким образом, помогает в лучшей обработке, так как все задачи идут вместе
- Также
await work()
неверно, ошибка компиляции, для этого требуется токен отмены как параметр I / p
- В текущем коде в идеале также следует ожидать
redirect(work)
, асинхронное ожидание должно происходить по всей цепочке вызовов
Модифицированная версия вашего кода
При условии, что вам нужно Task.WhenAll
, и вы выполняете асинхронную работу на основе ввода-вывода, ваш код будет следующим:
protected override async Task ExecuteAsync(CancellationToken stoppingQueueToken,CancellationToken stopWorkToken)
{
var work = await TaskQueue.DequeueAsync(stoppingQueueToken);
List<Task> taskList = new List<Task>();
while (!stoppingQueueToken.IsCancellationRequested)
{
taskList.Add(redirect(work, stopWorkToken));
}
await Task.WhenAll(taskList);
}
public async Task redirect(Func<CancellationToken, Task> work, CancellationToken stopWorkToken)
{
await work(stopWorkToken);
}
Я взял на себя смелость представить дополнительный маркер отмены для отмены работы, так как обработка работы начинается только один раз, когда завершается цикл, до этого он просто объединяет задачи, затем все они обрабатываются в фоновом режиме, принимая IO на максимум выгода. Здесь также отладчик будет блокировать, но для всех задач вместе один вызов
Если вы сохраняете текущий код, вам просто нужно внести следующие изменения:
protected override async Task ExecuteAsync(CancellationToken stoppingQueueToken)
{
var work = await TaskQueue.DequeueAsync(stoppingQueueToken);
List<Task> taskList = new List<Task>();
while (!stoppingQueueToken.IsCancellationRequested)
{
await redirect(work, stoppingQueueToken);
}
}
public async Task redirect(Func<CancellationToken, Task> work, CancellationToken stoppingQueueToken)
{
await work(stoppingQueueToken);
}
- Этот код выполняется один за другим, все еще асинхронно и освобождает вызывающий контекст, но не все вместе. Отладчик заблокирует, как вы уже видели.
Немного подробнее
Task
и Thread
не являются одинаковыми и взаимозаменяемыми, несколько задач могут выполняться в одном потоке
- Токен отмены является кооперативным, он не отменяет уже запущенную задачу, либо должен быть отменен до начала задачи, либо должен быть явно проверен и брошен для остановки выполнения задачи,
ThrowIfCancellationRequested