Voici le code:
Method finished Main finished
Je m'attends à ce que t.Wait () attende réellement la fin de la méthode AsyncTest et la sortie sera:
static async Task Main(string[] args)
{
var t = new Task(async () => await AsyncTest());
t.Start();
t.Wait();
Console.WriteLine("Main finished");
}
private static async Task AsyncTest()
{
Thread.Sleep(2000);
await Task.Delay(2000);
Console.WriteLine("Method finished");
}
En réalité, seule la sortie Main est terminée . Wait () est terminé juste au moment où vous appuyez sur wait Task.Delay (2000) dans AsyncTest. La même chose se produit si vous remplacez t.Wait () par await t / await Task.WhenAll (t) / Task.WaitAll (t) .
La solution est de réécrire la méthode en implémentation synchrone ou d'appeler Wait () directement sur AsyncTest (). Cependant, la question est de savoir pourquoi cela fonctionne d'une manière aussi étrange?
P.S. C'est une version simplifiée du code. J'essayais de réaliser une exécution de tâche différée. En réalité, l'objet Task est créé par une partie du programme, puis est exécuté plus tard par une autre partie après une condition spécifique.
UPD: Réécriture de var t = new Task (async () => wait AsyncTest ()) à var t = new Task (() => AsyncTest (). Wait ()) corrige également le problème. Bien que je ne comprends toujours pas pourquoi Task ne fonctionne pas correctement avec async / await inside delegate.
4 Réponses :
N'utilisez pas var task = new Task (...); task.Start (); , utilisez simplement Task.Run à la place.
Dans le premier cas, le constructeur Task (Action action) est utilisé pour que vous n'attendiez pas réellement la méthode async. Si vous inspectez real C # sans sucre de syntaxe asyn-await, vous verrez AsyncVoidMethodBuilder . Dans le cas de Task.Run , vous utilisez Task.Run (Func pour recevoir des tâches "réelles".
Donc, si vous voulez utiliser le constructeur, vous devez utiliser l'approche des commentaires: nouvelle tâche .
Comment cela répond-il à la question?
Une citation de @JonSkeet :
Votre méthode async renvoie simplement void, ce qui signifie qu'il n'y a pas de moyen simple de tout ce qui attend qu'il se termine. (Vous devriez presque toujours évitez d'utiliser les méthodes async void. Ils ne sont vraiment disponibles que pour le pour vous abonner à des événements.)
Alors regardez cette ligne de votre code:
static async Task Main(string[] args)
{
// best way to do it
await AsyncTest();
Console.WriteLine("Main finished");
}
private static async Task AsyncTest()
{
// Don't use thread sleep, await task delay is fine
// Thread.Sleep(2000);
await Task.Delay(2000);
Console.WriteLine("Method finished");
}
Regardez la signature des constructeurs de Task :
public Task(Action action);
public Task(Action action, CancellationToken cancellationToken);
public Task(Action action, TaskCreationOptions creationOptions);
public Task(Action<object> action, object state);
public Task(Action action, CancellationToken cancellationToken, TaskCreationOptions creationOptions);
public Task(Action<object> action, object state, CancellationToken cancellationToken);
public Task(Action<object> action, object state, TaskCreationOptions creationOptions);
public Task(Action<object> action, object state, CancellationToken cancellationToken, TaskCreationOptions creationOptions);
Toutes sont des Actions et comme vous le savez, Action a un type de retour void . p >
var t = new Task(async () => await AsyncTest());
Bien que cette réponse ait du sens, vous semblez avoir copié l'explication de la réponse de ce Jon .
@WiktorZychla Oui, le premier paragraphe est de la légende JonSkeet, j'ai modifié la réponse récemment mais j'ai quitté l'ordinateur portable et j'ai oublié d'envoyer les modifications.
vérifiez ceci
public static async Task Main(string[] args)
{
var t = Task.Factory.StartNew(async () => await AsyncTest());
//t.Start();
t.Result.Wait();
t.Wait();
Console.WriteLine("Main finished");
}
private static async Task AsyncTest()
{
//Thread.Sleep(2000);
await Task.Delay(2000);
Console.WriteLine("Method finished");
}
et ce lien
La question porte sur: pourquoi le motif var t = new Task (...); t.Wait (); ne fonctionne pas comme prévu.
Je ne comprends toujours pas pourquoi Task ne fonctionne pas correctement avec async / await inside delegate.
Parce que le constructeur
Taskn'est utilisé que pour créer Déléguer des tâches - c'est-à-dire des tâches qui représentent du code synchrone à exécuter. Comme le code est synchrone, votre lambdaasyncest traité comme un lambdaasync void, ce qui signifie que l'instanceTaskn'attendra pas de manière asynchroneAsyncTestpour terminer.Plus précisément, le constructeur de
Taskne doit jamais, jamais être utilisé dans un code, n'importe où, pour quelque raison que ce soit . Il n'a littéralement aucun cas d'utilisation valide.Un bon remplacement de
Task.TaskestTask.Run, ce qui fait comprendreasynclambdas.Dans mon vrai programme, Task a différé son exécution. L'objet de tâche est créé à un endroit, puis plus tard après une condition spécifique exécutée par une autre partie du programme.
Dans ce cas, utilisez un délégué asynchrone . Plus précisément,
Func.static async Task Main(string[] args) { Func<Task> func = AsyncTest; // Later, when we're ready to run. await func(); Console.WriteLine("Main finished"); } private static async Task AsyncTest() { Thread.Sleep(2000); await Task.Delay(2000); Console.WriteLine("Method finished"); }
N'utilisez pas
var task = new Task (...); task.Start ();, utilisez simplementTask.Runà la place.Dans mon vrai programme, Task a différé l'exécution. L'objet de tâche est créé à un endroit, puis plus tard après une condition spécifique exécutée par une autre partie du programme.
Possibilité de duplication de Task.Wait () n'attendant pas la fin de la tâche a>
@ IllidanS4: ça ne ressemble pas, la méthode renvoie correctement une tâche. Il y a quelque chose d'inattendu à propos du
Task.Delayici, si vous le commentez (et laissezThread.Sleep), il semble fonctionner comme prévu.@WiktorZychla Le
AsyncTestrenvoie une tâche, mais leasync () => wait AsyncTest ()lambda la jette car il s'agit d'uneAction(void) lui-même.() => AsyncTest (). Wait ()le "corrigerait" (ce serait toujours faux à cause duWait ()).@GSerg: cela ne devrait pas avoir d'importance. Le constructeur n'attend même pas de tâches. C'est la tâche retournée par le constructeur que les OP essaient d'attendre, pas le lambda interne.
@WiktorZychla Oui, l'OP attend la tâche créée par le constructeur, mais cette tâche n'est pas en mesure d'attendre de manière significative son propre argument lambda, donc par extension, l'OP ne peut pas le faire non plus. Vous n'êtes pas censé passer
async () =>à un constructeur de tâche.@ IllidanS4: cela peut ressembler à un duplicata du lien , mais c'est un peu différent. Je pensais que ça devrait être OK tant que je n'utilise pas async void dans la signature de méthode.
@GSerg: Je le sais, je comprends aussi quel est le problème ici. Je crois que cette phrase de la vôtre Vous n'êtes pas censé passer async () => à un constructeur de tâche devrait être affichée une réponse réelle. Je veux dire, le seul moyen de réaliser ce que OP veut est d'appeler simplement le
AsyncTestsi nécessaire et de leAttendre, sans l'envelopper dans une autreTâche.var t1 = new Task (async () => wait AsyncTest ()); var t2 = t1.Unwrap (); t1.Start (); t2.Wait (); @GSerg vous avez raison. Ce code
var t = Task.Run (async () => await AsyncTest ())utiliseAsyncTaskMethodBuilder, mais celui-civar t = new Task (async ( ) => wait AsyncTest ())utiliseAsyncVoidMethodBuilder.@PetSerAl Ouais. Remplacez simplement le dernier
Waitparawait t2.@mtkachenko: c'est pourquoi j'ai voté pour votre toute première réponse car elle était correcte.
@WiktorZychla Ce n'est peut-être pas le cas.
Task.Rundémarre la tâche immédiatement, l'OP veut apparemment pouvoir avoir une tâche pas encore en cours d'exécution qu'ils peuventDémarrer ()et attendre plus tard.