J'ai fait ce test unitaire et je ne comprends pas pourquoi "attendre Task.Delay ()" n'attend pas!
[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");
}
Voici la sortie du test unitaire:
Comment est-ce possible qu'un "attente" n'attende pas, et le fil principal continue?
Task.Run()
après le premierConsole.WriteLine
?Réponses:
new Task(async () =>
Une tâche ne prend pas un
Func<Task>
, mais unAction
. Il appellera votre méthode asynchrone et s'attendra à ce qu'elle se termine lorsqu'elle reviendra. Mais ce n'est pas le cas. Il renvoie une tâche. Cette tâche n'est pas attendue par la nouvelle tâche. Pour la nouvelle tâche, le travail est effectué une fois la méthode retournée.Vous devez utiliser la tâche qui existe déjà au lieu de l'envelopper dans une nouvelle tâche:
la source
Task.Run(async() => ... )
est également une optionmyTask.Start();
unInvalidOperationException
myTask.Start()
produirait une exception pour son alternative et devrait être supprimé lors de l'utilisationTask.Run(...)
. Il n'y a aucune erreur dans votre solution.Le problème est que vous utilisez la
Task
classe non générique , qui n'est pas destinée à produire un résultat. Ainsi, lorsque vous créez l'Task
instance en passant un délégué asynchrone:... le délégué est traité comme
async void
. Unasync void
n'est pas unTask
, il ne peut pas être attendu, son exception ne peut pas être gérée, et c'est une source de milliers de questions posées par des programmeurs frustrés ici dans StackOverflow et ailleurs. La solution consiste à utiliser laTask<TResult>
classe générique , car vous souhaitez renvoyer un résultat, et le résultat en est un autreTask
. Vous devez donc créer unTask<Task>
:Maintenant, lorsque vous
Start
l'extérieur,Task<Task>
il sera terminé presque instantanément parce que son travail consiste simplement à créer l'intérieurTask
. Vous devrez également attendre l'intérieurTask
. Voici comment cela peut être fait:Vous avez deux alternatives. Si vous n'avez pas besoin d'une référence explicite à l'intérieur,
Task
vous pouvez simplement attendreTask<Task>
deux fois l'extérieur :... ou vous pouvez utiliser la méthode d'extension intégrée
Unwrap
qui combine les tâches externes et internes en une seule:Ce déballage se produit automatiquement lorsque vous utilisez la
Task.Run
méthode beaucoup plus populaire qui crée des tâches à chaud, de sorte que leUnwrap
n'est pas utilisé très souvent de nos jours.Si vous décidez que votre délégué asynchrone doit renvoyer un résultat, par exemple a
string
, vous devez déclarer lamyTask
variable comme étant de typeTask<Task<string>>
.Remarque: je n'approuve pas l'utilisation de
Task
constructeurs pour créer des tâches froides. Comme une pratique est généralement mal vue, pour des raisons que je ne connais pas vraiment, mais probablement parce qu'elle est utilisée si rarement qu'elle a le potentiel de surprendre d'autres utilisateurs / mainteneurs / réviseurs ignorants du code par surprise.Conseil général: soyez prudent chaque fois que vous fournissez un délégué asynchrone comme argument d'une méthode. Cette méthode devrait idéalement attendre un
Func<Task>
argument (ce qui signifie qu'il comprend les délégués asynchrones), ou au moins unFunc<T>
argument (ce qui signifie qu'au moins le généréTask
ne sera pas ignoré). Dans le cas malheureux que cette méthode accepte unAction
, votre délégué sera traité commeasync void
. C'est rarement ce que vous voulez, voire jamais.la source
la source
await
, le retard de tâche ne retardera pas réellement cette tâche, non? Vous avez supprimé la fonctionnalité.