246 votes

await vs Task.Wait - Impasse ?

Je ne comprends pas bien la différence entre Task.Wait y await .

J'ai quelque chose de similaire aux fonctions suivantes dans un service WebAPI ASP.NET :

public class TestController : ApiController
{
    public static async Task<string> Foo()
    {
        await Task.Delay(1).ConfigureAwait(false);
        return "";
    }

    public async static Task<string> Bar()
    {
        return await Foo();
    }

    public async static Task<string> Ros()
    {
        return await Bar();
    }

    // GET api/test
    public IEnumerable<string> Get()
    {
        Task.WaitAll(Enumerable.Range(0, 10).Select(x => Ros()).ToArray());

        return new string[] { "value1", "value2" }; // This will never execute
    }
}

Get sera dans l'impasse.

Qu'est-ce qui pourrait causer cela ? Pourquoi cela ne pose-t-il pas de problème si j'utilise une attente bloquante au lieu de await Task.Delay ?

346voto

Stephen Cleary Points 91731

Wait y await - bien que similaires sur le plan conceptuel, sont en réalité complètement différentes.

Wait bloquera de manière synchrone jusqu'à ce que la tâche soit terminée. Le thread actuel est donc littéralement bloqué en attendant que la tâche se termine. En règle générale, vous devriez utiliser " async tout en bas", c'est-à-dire ne pas bloquer sur async code. Sur mon blog, j'entre dans les détails de ce qui suit comment le blocage d'un code asynchrone provoque un blocage des données .

await attendra de manière asynchrone que la tâche soit terminée. Cela signifie que la tâche actuelle méthode est "mis en pause" (son état est capturé) et la méthode renvoie une tâche incomplète à son appelant. Plus tard, lorsque le await se termine, le reste de la méthode est programmé comme une continuation.

Vous avez également mentionné un "bloc coopératif", par lequel je suppose que vous voulez dire une tâche que vous Wait peut s'exécuter sur le fil d'attente. Il y a des situations où cela peut arriver, mais c'est une optimisation. Il existe de nombreuses situations où il no puede se produisent, par exemple si la tâche est destinée à un autre planificateur, si elle a déjà été lancée ou s'il s'agit d'une tâche non codée (comme dans votre exemple de code) : Wait ne peut pas exécuter le Delay en ligne car il n'y a pas de code pour cela).

Vous pouvez trouver mon async / await introduction utile.

19voto

Ayushmati Points 922

D'après ce que j'ai lu de différentes sources :

Un site await ne bloque pas le thread sur lequel elle est exécutée. Au lieu de cela, elle amène le compilateur à signer le reste de l'expression async comme une continuation de la tâche attendue. Le contrôle revient ensuite à l'appelant de la méthode async méthode. Lorsque la tâche se termine, elle invoque sa continuation, et l'exécution de la méthode de la async reprend là où elle s'est arrêtée.

Pour attendre un seul task pour terminer, vous pouvez appeler son Task.Wait méthode. Un appel à la méthode Wait bloque le thread appelant jusqu'à ce que l'instance de classe unique ait terminé son exécution. La méthode sans paramètre Wait() est utilisée pour attendre sans condition la fin d'une tâche. La tâche simule le travail en appelant la méthode Thread.Sleep pour dormir pendant deux secondes.

Cet article est aussi une bonne lecture.

5voto

user1785960 Points 37

Certains faits importants n'ont pas été donnés dans les autres réponses :

"async await" est plus complexe au niveau du CIL et coûte donc de la mémoire et du temps CPU.

Toute tâche peut être annulée si le temps d'attente est inacceptable.

Dans le cas "async await", nous n'avons pas de gestionnaire pour une telle tâche pour l'annuler ou la surveiller.

L'utilisation de Task est plus flexible que "async await".

Toute fonctionnalité sync peut être enveloppée par async.

public async Task<ActionResult> DoAsync(long id) 
{ 
    return await Task.Run(() => { return DoSync(id); } ); 
} 

"async await" génèrent de nombreux problèmes. Nous ne savons pas aujourd'hui si l'instruction await sera atteinte sans débogage du runtime et du contexte. Si la première attente n'est pas atteinte, tout est bloqué . Parfois, même si l'attente semble être atteinte, tout reste bloqué :

https://github.com/dotnet/runtime/issues/36063

Je ne vois pas pourquoi je dois vivre avec la duplication du code pour les méthodes sync et async ou utiliser des hacks.

Conclusion : Créer des tâches manuellement et les contrôler est beaucoup mieux. Le gestionnaire de tâches donne plus de contrôle. Nous pouvons surveiller les tâches et les gérer :

https://github.com/lsmolinski/MonitoredQueueBackgroundWorkItem

Désolé pour mon anglais.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X