Eu fiz esse teste de unidade e não entendo por que o "aguardar Task.Delay ()" não espera!
[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");
}
Aqui está a saída do teste de unidade:
Como isso é possível, um "aguardar" não espera e o thread principal continua?
Task.Run()
após o primeiroConsole.WriteLine
?Respostas:
new Task(async () =>
Uma tarefa não leva a
Func<Task>
, mas simAction
. Ele chamará seu método assíncrono e esperará que ele termine quando retornar. Mas isso não acontece. Retorna uma tarefa. Essa tarefa não é aguardada pela nova tarefa. Para a nova tarefa, o trabalho é realizado assim que o método é retornado.Você precisa usar a tarefa que já existe, em vez de agrupá-la em uma nova tarefa:
fonte
Task.Run(async() => ... )
também é uma opçãomyTask.Start();
irá aumentar umInvalidOperationException
myTask.Start()
traria uma exceção para sua alternativa e deve ser removido ao usarTask.Run(...)
. Não há erro na sua solução.O problema é que você está usando a
Task
classe não genérica , que não pretende produzir um resultado. Portanto, quando você cria aTask
instância passando um delegado assíncrono:... o delegado é tratado como
async void
. Umasync void
não é umTask
, não pode ser esperado, sua exceção não pode ser tratada e é uma fonte de milhares de perguntas feitas por programadores frustrados aqui no StackOverflow e em outros lugares. A solução é usar aTask<TResult>
classe genérica , porque você deseja retornar um resultado, e o resultado é outroTask
. Então você tem que criar umTask<Task>
:Agora, quando você for
Start
externoTask<Task>
, será concluído quase instantaneamente, porque seu trabalho é apenas criar o interiorTask
. Você terá que aguardar o interiorTask
também. É assim que isso pode ser feito:Você tem duas alternativas. Se você não precisar de uma referência explícita ao interno
Task
, poderá aguardar o externoTask<Task>
duas vezes:... ou você pode usar o método de extensão
Unwrap
interno que combina as tarefas externa e interna em uma:Esse desempacotamento acontece automaticamente quando você usa o
Task.Run
método muito mais popular que cria tarefas quentes, portanto, eleUnwrap
não é usado com muita frequência atualmente.Caso você decida que seu representante assíncrono deve retornar um resultado, por exemplo
string
, a , declare que amyTask
variável é do tipoTask<Task<string>>
.Nota: Eu não apoio o uso de
Task
construtores para criar tarefas frias. Como uma prática geralmente é desaprovada, por motivos que eu realmente não conheço, mas provavelmente porque é usada tão raramente que tem o potencial de capturar outros usuários / mantenedores / revisores desconhecidos do código de surpresa.Conselho geral: tenha cuidado sempre que estiver fornecendo um delegado assíncrono como argumento para um método. Idealmente, esse método deve esperar um
Func<Task>
argumento (o que significa que compreende delegados assíncronos) ou pelo menos umFunc<T>
argumento (o que significa que pelo menos o geradoTask
não será ignorado). No infeliz caso em que esse método aceite umAction
, seu delegado será tratado comoasync void
. Isso raramente é o que você deseja, se é que alguma vez.fonte
fonte
await
atraso da tarefa, na verdade, não atrasará essa tarefa, certo? Você removeu a funcionalidade.