Кто-нибудь может объяснить, почему этот код просто заходит в тупик после срабатывания WhenAll?
Основной код:
class AsyncTests
{
public async void Start()
{
Console.WriteLine($"Thread:{Thread.CurrentThread.ManagedThreadId} - starting whole process, calling await DoWork1()");
await Task.WhenAll(DoWork1(), DoWork2());
Console.WriteLine($"Thread:{Thread.CurrentThread.ManagedThreadId} - finished awaiting DoWork1 and DoWork2");
}
public async Task DoWork1()
{
Console.WriteLine($"Thread:{Thread.CurrentThread.ManagedThreadId} - starting DoWork1");
await DoNothing();
Console.WriteLine($"Thread:{Thread.CurrentThread.ManagedThreadId} - finished DoWork1");
}
public async Task DoWork2()
{
Console.WriteLine($"Thread:{Thread.CurrentThread.ManagedThreadId} - starting DoWork2");
await DoNothing();
Console.WriteLine($"Thread:{Thread.CurrentThread.ManagedThreadId} - finished DoWork2");
}
public Task DoNothing() { return new Task(() => { /* do nothing */ }); }
}
Код управления программой:
static void Main(string[] args)
{
var x = new AsyncTests();
Console.WriteLine($"Thread:{Thread.CurrentThread.ManagedThreadId} - Main ... calling Start()");
Task.Run(() => x.Start());
Console.WriteLine($"Thread:{Thread.CurrentThread.ManagedThreadId} - Main ... start is running");
Console.ReadKey();
}
Выход:
Thread:1 - Main ... calling Start()
Thread:1 - Main ... start is running
Thread:4 - starting whole process, calling await DoWork1()
Thread:4 - starting DoWork1
Thread:4 - starting DoWork2
ОБНОВЛЕНИЕ
Чтобы сделать это немного понятнее, давайте изменим его так, чтобы DoNothing фактически вызывал Thread.Sleep (2000), и моя цель - запустить два спящих потока одновременно и хотеть использовать шаблон async / await для достижения этой цели.
Если я изменю «DoNothing» на асинхронную задачу, которая выполняет спящий режим, то мне скажут, что там нужны операторы await
. Что означает, что мне нужно написать еще один async
метод, которого следует ожидать. Итак, как лучше всего завершить эту цепочку вызовов с точки зрения операторов?
Может ли кто-нибудь показать пример передового опыта того, как достичь вышеуказанного?
3 ответа
Вы никогда не начинаете свою задачу в DoNothing.
Разделение создания и выполнения задач
Класс Task также предоставляет конструкторы, которые инициализируют задачу, но не планируют ее выполнение. По соображениям производительности метод Task.Run или TaskFactory.StartNew является предпочтительным механизмом для создания и планирования вычислительных задач, но для сценариев, где создание и планирование должны быть разделены, можно использовать конструкторы, а затем вызвать метод Task.Start для планирования задача для выполнения в более позднее время.
Вместо того, чтобы создавать задание для возврата, пусть язык сделает это за вас.
public async Task DoNothing() { }
Вышеприведенное фактически ничего не делает и вернет Task
, который находится в завершенном состоянии и его можно ожидать.
То, как вы сейчас это делаете, Задача создается, но она никогда не запускается и не устанавливается в Завершено, поэтому ожидание ее навсегда заблокирует программу.
Просто чтобы дополнить уже предоставленные ответы и комментарии, я хотел показать пример кода, работающий так, как я задумал в тесте. Он показывает поток выполнения + для информации, идентификатор управляемого потока выполнения в определенные моменты времени.
Основной код:
class AsyncTests
{
public async Task StartAsync()
{
Console.WriteLine($"{DateTime.Now:HH:mm:ss.fff} - Thread:{Thread.CurrentThread.ManagedThreadId} - starting whole process, calling await DoWork1Async()");
await Task.WhenAll(DoWork1Async(), DoWork2Async());
Console.WriteLine($"{DateTime.Now:HH:mm:ss.fff} - Thread:{Thread.CurrentThread.ManagedThreadId} - finished awaiting DoWork1Async and DoWork2Async");
}
public async Task DoWork1Async()
{
Console.WriteLine($"{DateTime.Now:HH:mm:ss.fff} - Thread:{Thread.CurrentThread.ManagedThreadId} - starting DoWork1Async");
await Sleep();
Console.WriteLine($"{DateTime.Now:HH:mm:ss.fff} - Thread:{Thread.CurrentThread.ManagedThreadId} - finished DoWork1Async");
}
public async Task DoWork2Async()
{
Console.WriteLine($"{DateTime.Now:HH:mm:ss.fff} - Thread:{Thread.CurrentThread.ManagedThreadId} - starting DoWork2Async");
await Sleep();
Console.WriteLine($"{DateTime.Now:HH:mm:ss.fff} - Thread:{Thread.CurrentThread.ManagedThreadId} - finished DoWork2Async");
}
public async Task Sleep()
{
await Task.Delay(2000);
}
}
Вызывающий код (обратите внимание, что для выполнения этой операции асинхронно, мне пришлось опустить оператор await
, который вызывает предупреждение consider applying the 'await' operator
при вызове метода StartAsync()
.
static void Main(string[] args)
{
var x = new AsyncTests();
Console.WriteLine($"{DateTime.Now:HH:mm:ss.fff} - {Thread.CurrentThread.ManagedThreadId} - Main ... calling Start()");
x.StartAsync();
Console.WriteLine($"{DateTime.Now:HH:mm:ss.fff} - {Thread.CurrentThread.ManagedThreadId} - Main ... start is running");
Console.ReadKey();
}
Наконец, вывод - который, как и ожидалось, показывает выполнение кода / управление, возвращающееся в места, ожидаемые для действительно асинхронной операции. Как и ожидалось, для запуска спящего режима использовались два разных потока пула.
10:43:36.515 - 1 - Main ... calling Start()
10:43:36.546 - Thread:1 - starting whole process, calling await DoWork1Async()
10:43:36.547 - Thread:1 - starting DoWork1Async
10:43:36.561 - Thread:1 - starting DoWork2Async
10:43:36.562 - 1 - Main ... start is running
10:43:38.581 - Thread:4 - finished DoWork2Async
10:43:38.582 - Thread:5 - finished DoWork1Async
10:43:38.582 - Thread:5 - finished awaiting DoWork1Async and DoWork2Async
Похожие вопросы
Новые вопросы
c#
C # (произносится как «резкий») - это высокоуровневый, статически типизированный язык программирования с несколькими парадигмами, разработанный Microsoft. Код C # обычно нацелен на семейство инструментов и сред выполнения Microsoft .NET, включая, среди прочего, .NET Framework, .NET Core и Xamarin. Используйте этот тег для вопросов о коде, написанном на C # или в формальной спецификации C #.