web-dev-qa-db-fra.com

Async toujours en attente pour l'activation

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?

37
BanksySan

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:

  • Annulé
  • Défectueux
  • RanToCompletion
  • En attente d'activation

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).

46
Lukazoid

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:

enter image description here

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.

18
Selman Genç

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.
0
someone else