Я провел этот модульный тест, и я не понимаю, почему "await Task.Delay ()" не ждет!
[TestMethod]
public async Task SimpleTest()
{
bool isOK = false;
Task myTask = new Task(async () =>
{
Console.WriteLine("Task.BeforeDelay");
await Task.Delay(1000);
Console.WriteLine("Task.AfterDelay");
isOK = true;
Console.WriteLine("Task.Ended");
});
Console.WriteLine("Main.BeforeStart");
myTask.Start();
Console.WriteLine("Main.AfterStart");
await myTask;
Console.WriteLine("Main.AfterAwait");
Assert.IsTrue(isOK, "OK");
}
Вот результат модульного теста:
Как это возможно, «ожидание» не ждет, и основной поток продолжается?
Task.Run()
после первогоConsole.WriteLine
?Ответы:
new Task(async () =>
Задача не занимает
Func<Task>
, аAction
. Он будет вызывать ваш асинхронный метод и ожидать, что он завершится, когда вернется. Но это не так. Это возвращает задачу. Эта задача не ожидается новой задачей. Для новой задачи задание выполняется после возврата метода.Вам нужно использовать задачу, которая уже существует, вместо того, чтобы переносить ее в новую задачу:
источник
Task.Run(async() => ... )
также вариантmyTask.Start();
, поднятьInvalidOperationException
myTask.Start()
что даст исключение для его альтернативы и должен быть удален при использованииTask.Run(...)
. Там нет ошибки в вашем решении.Проблема в том, что вы используете неуниверсальный
Task
класс, который не предназначен для получения результата. Поэтому, когда вы создаетеTask
экземпляр, передающий асинхронный делегат:... делегат рассматривается как
async void
. Anasync void
не являетсяTask
, его нельзя ожидать, его исключение не может быть обработано, и это источник тысяч вопросов, задаваемых разочарованными программистами здесь, в StackOverflow и в других местах. Решение состоит в том, чтобы использовать универсальныйTask<TResult>
класс, потому что вы хотите вернуть результат, а результат - другойTask
. Итак, вы должны создатьTask<Task>
:Теперь, когда вы
Start
внешнее,Task<Task>
оно будет завершено почти мгновенно, потому что его задача - просто создать внутреннееTask
. Затем вам придется ждать и внутреннегоTask
. Вот как это можно сделать:У вас есть две альтернативы. Если вам не нужна явная ссылка на внутреннее,
Task
тогда вы можете просто ждать внешнегоTask<Task>
дважды:... или вы можете использовать встроенный метод расширения,
Unwrap
который объединяет внешние и внутренние задачи в одну:Это развертывание происходит автоматически, когда вы используете гораздо более популярный
Task.Run
метод, который создает горячие задачи, поэтому вUnwrap
настоящее время он используется не очень часто.Если вы решите, что ваш асинхронный делегат должен вернуть результат, например, a
string
, то вы должны объявитьmyTask
переменную типаTask<Task<string>>
.Примечание: я не поддерживаю использование
Task
конструкторов для создания холодных задач. Как правило, практика не одобряется по причинам, которые я на самом деле не знаю, но, вероятно, потому, что она используется настолько редко, что потенциально может застать врасплох других неосведомленных пользователей / сопровождающих / рецензентов кода.Общий совет: будьте осторожны каждый раз, когда вы предоставляете асинхронный делегат в качестве аргумента метода. Этот метод в идеале должен ожидать
Func<Task>
аргумент (что означает, что он понимает асинхронные делегаты) или, по крайней мере,Func<T>
аргумент (то есть, что, по крайней мере, сгенерированныйTask
не будет проигнорирован). В неудачном случае, когда этот метод принимаетAction
, ваш делегат будет рассматриваться какasync void
. Это редко то, что вы хотите, если когда-либо.источник
источник
await
, задержка задачи фактически не задержит эту задачу, верно? Вы удалили функциональность.