J'essaie de comprendre en quoi consistent les mots clés async
& await
, mais le résultat obtenu n'est pas ce que j'attendais.
L'application console est la suivante:
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Foo called");
var result = Foo(5);
while (result.Status != TaskStatus.RanToCompletion)
{
Console.WriteLine("Thread ID: {0}, Status: {1}", Thread.CurrentThread.ManagedThreadId, result.Status);
Task.Delay(100).Wait();
}
Console.WriteLine("Result: {0}", result.Result);
Console.WriteLine("Finished.");
Console.ReadKey(true);
}
private static async Task<string> Foo(int seconds)
{
return await Task.Run(() =>
{
for (int i = 0; i < seconds; i++)
{
Console.WriteLine("Thread ID: {0}, second {1}.", Thread.CurrentThread.ManagedThreadId, i);
Task.Delay(TimeSpan.FromSeconds(1)).Wait();
}
return "Foo Completed.";
});
}
}
La sortie est:
Foo called
Thread ID: 10, Status: WaitingForActivation
Thread ID: 6, second 0.
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 6, second 1.
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 6, second 2.
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 6, second 3.
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 6, second 4.
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Result: Foo Completed.
Finished..
Je m'attendais à voir le statut changer de WaitingForActivation
une fois la méthode démarrée.
Comment peut-il rester dans cet état et être actif?
Pour ma réponse, il convient de rappeler que l'énumération TPL ( Task-Parallel-Library ), Task
et TaskStatus
a été introduite avant les mots clés async-wait et que les mots clés async-wait n'étaient pas la motivation initiale du mot TPL.
Dans le contexte des méthodes marquées par async
, la Task
résultante n'est pas une Task
représentant l'exécution de la méthode, mais une Task
pour la suite de la méthode.
Ceci est seulement capable d'utiliser quelques états possibles:
Je comprends que Running
pourrait sembler avoir été un meilleur défaut que WaitingForActivation
, mais cela peut être trompeur, car la plupart du temps, une méthode asynchrone execute n'est pas en cours d'exécution (c'est-à-dire await
autre chose ). L’autre option aurait peut-être été d’ajouter une nouvelle valeur à TaskStatus
; toutefois, cela aurait pu constituer un changement radical pour les applications et les bibliothèques existantes.
Tout cela est très différent de l'utilisation de Task.Run
qui fait partie de la TPL d'origine, cela permet d'utiliser toutes les valeurs possibles de l'énumération TaskStatus
.
Si vous souhaitez suivre l'état d'une méthode asynchrone, jetez un coup d'œil à l'interface IProgress(T)
, cela vous permettra de rendre compte de la progression en cours. Ce billet de blog, Async dans la version 4.5: activation de la progression et de l'annulation dans les API asynchrones fournira des informations supplémentaires sur l'utilisation de l'interface IProgress(T)
.
La raison en est votre result
attribuée au retour Task
qui représente la continuation de votre méthode, et vous avez une tâche différente dans votre méthode en cours d'exécution, si vous affectez directement une tâche comme celle-ci, vous obtiendrez les résultats attendus:
var task = Task.Run(() =>
{
for (int i = 10; i < 432543543; i++)
{
// just for a long job
double d3 = Math.Sqrt((Math.Pow(i, 5) - Math.Pow(i, 2)) / Math.Sin(i * 8));
}
return "Foo Completed.";
});
while (task.Status != TaskStatus.RanToCompletion)
{
Console.WriteLine("Thread ID: {0}, Status: {1}", Thread.CurrentThread.ManagedThreadId,task.Status);
}
Console.WriteLine("Result: {0}", task.Result);
Console.WriteLine("Finished.");
Console.ReadKey(true);
La output
:
Considérez ceci pour une meilleure explication: _ Vous avez une méthode Foo
, disons-la tâche A, et vous avez une Task
, disons-la tâche B, Maintenant, la tâche en cours d'exécution est tâche B, votre tâche A en attente de {résultat de la tâche B. Et vous assénez votre variable de résultat à votre retournant Task
qui est tâche A, parce que la tâche B ne renvoie pas de tâche, elle retourne une string
. Considère ceci:
Si vous définissez votre résultat comme ceci:
Task result = Foo(5);
Vous n'obtiendrez aucune erreur. Mais si vous le définissez comme ceci:
string result = Foo(5);
Tu auras:
Impossible de convertir implicitement le type 'System.Threading.Tasks.Task' en 'chaîne'
Mais si vous ajoutez un mot clé await
:
string result = await Foo(5);
Encore une fois, vous n'obtiendrez aucune erreur.Parce qu'il attendra le résultat (chaîne) et l'affectera à votre variable de résultat.Pour la dernière chose, tenez compte de ceci, si vous ajoutez deux tâches à votre méthode Foo:
private static async Task<string> Foo(int seconds)
{
await Task.Run(() =>
{
for (int i = 0; i < seconds; i++)
{
Console.WriteLine("Thread ID: {0}, second {1}.", Thread.CurrentThread.ManagedThreadId, i);
Task.Delay(TimeSpan.FromSeconds(1)).Wait();
}
// in here don't return anything
});
return await Task.Run(() =>
{
for (int i = 0; i < seconds; i++)
{
Console.WriteLine("Thread ID: {0}, second {1}.", Thread.CurrentThread.ManagedThreadId, i);
Task.Delay(TimeSpan.FromSeconds(1)).Wait();
}
return "Foo Completed.";
});
}
Et si vous exécutez l'application, vous obtiendrez les mêmes résultats. (WaitingForActivation) Car maintenant, votre tâche A attend ces deux tâches.
J'ai eu le même problème. Les réponses m'ont mis sur la bonne voie. Le problème est donc que les fonctions marquées async ne renvoient pas une tâche de la fonction elle-même comme prévu (mais une autre tâche de continuation de la fonction).
C'est donc les mots-clés "wait" et "async" qui gâchent tout. La solution la plus simple consiste alors simplement à les supprimer. Ensuite, cela fonctionne comme prévu. Un péché:
static void Main(string[] args)
{
Console.WriteLine("Foo called");
var result = Foo(5);
while (result.Status != TaskStatus.RanToCompletion)
{
Console.WriteLine("Thread ID: {0}, Status: {1}", Thread.CurrentThread.ManagedThreadId, result.Status);
Task.Delay(100).Wait();
}
Console.WriteLine("Result: {0}", result.Result);
Console.WriteLine("Finished.");
Console.ReadKey(true);
}
private static Task<string> Foo(int seconds)
{
return Task.Run(() =>
{
for (int i = 0; i < seconds; i++)
{
Console.WriteLine("Thread ID: {0}, second {1}.", Thread.CurrentThread.ManagedThreadId, i);
Task.Delay(TimeSpan.FromSeconds(1)).Wait();
}
return "Foo Completed.";
});
}
Quelles sorties:
Foo called
Thread ID: 1, Status: WaitingToRun
Thread ID: 3, second 0.
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 3, second 1.
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 3, second 2.
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 3, second 3.
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 3, second 4.
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Result: Foo Completed.
Finished.