web-dev-qa-db-fra.com

Comment attendre que la méthode asynchrone se termine?

J'écris une application WinForms qui transfère des données à un périphérique de classe USB HID. Mon application utilise l'excellente bibliothèque générique HID v6.0 que l'on peut trouver ici . En un mot, lorsque j’ai besoin d’écrire des données sur le périphérique, c’est le code qui s’appelle:

private async void RequestToSendOutputReport(List<byte[]> byteArrays)
{
    foreach (byte[] b in byteArrays)
    {
        while (condition)
        {
            // we'll typically execute this code many times until the condition is no longer met
            Task t = SendOutputReportViaInterruptTransfer();
            await t;
        }

        // read some data from device; we need to wait for this to return
        RequestToGetInputReport();
    }
}

Lorsque mon code quitte la boucle while, je dois lire certaines données de l'appareil. Cependant, l'appareil ne pouvant pas répondre immédiatement, je dois donc attendre le retour de cet appel avant de continuer. Dans sa version actuelle, RequestToGetInputReport () est déclaré comme ceci:

private async void RequestToGetInputReport()
{
    // lots of code prior to this
    int bytesRead = await GetInputReportViaInterruptTransfer();
}

Pour ce que ça vaut, la déclaration de GetInputReportViaInterruptTransfer () ressemble à ceci:

internal async Task<int> GetInputReportViaInterruptTransfer()

Malheureusement, je ne connais pas très bien le fonctionnement des nouvelles technologies async/wait de .NET 4.5. J'ai lu un peu plus tôt le mot-clé wait, ce qui m'a donné l'impression que l'appel à GetInputReportViaInterruptTransfer () à l'intérieur de RequestToGetInputReport () serait attendu (et peut-être le fait-il?), Mais il ne semble pas que l'appel à RequestToGetInputReport () lui-même attend parce que je semble rentrer dans la boucle while presque immédiatement?

Quelqu'un peut-il clarifier le comportement que je vois?

124
bmt22033

Évitez async void. Demandez à vos méthodes de retourner Task au lieu de void. Ensuite, vous pouvez await eux.

Comme ça:

private async Task RequestToSendOutputReport(List<byte[]> byteArrays)
{
    foreach (byte[] b in byteArrays)
    {
        while (condition)
        {
            // we'll typically execute this code many times until the condition is no longer met
            Task t = SendOutputReportViaInterruptTransfer();
            await t;
        }

        // read some data from device; we need to wait for this to return
        await RequestToGetInputReport();
    }
}

private async Task RequestToGetInputReport()
{
    // lots of code prior to this
    int bytesRead = await GetInputReportViaInterruptTransfer();
}
117
Stephen Cleary

La chose la plus importante à savoir sur async et await est que await n’attend pas la appel à compléter. Ce que await fait, c'est renvoyer le résultat de l'opération immédiatement et de manière synchrone si l'opération est déjà terminée ou, si ce n'est pas le cas, pour planifier une continuation afin d'exécuter le reste de la méthode async, puis de redonner le contrôle à l'appelant. Lorsque l'opération asynchrone est terminée, la fin programmée est exécutée.

La réponse à la question spécifique dans le titre de votre question consiste à bloquer sur la valeur de retour d'une méthode async (qui doit être de type Task ou Task<T>) en appelant un Wait approprié. méthode:

public static async Task<Foo> GetFooAsync()
{
    // Start asynchronous operation(s) and return associated task.
    ...
}

public static Foo CallGetFooAsyncAndWaitOnResult()
{
    var task = GetFooAsync();
    task.Wait(); // Blocks current thread until GetFooAsync task completes
                 // For pedagogical use only: in general, don't do this!
    var result = task.Result;
    return result;
}

Dans cet extrait de code, CallGetFooAsyncAndWaitOnResult est un wrapper synchrone autour de la méthode asynchrone GetFooAsync. Cependant, ce modèle doit être en grande partie évité car il bloquera un thread complet du pool de threads pendant toute la durée de l'opération asynchrone. Il s’agit d’une utilisation inefficace des divers mécanismes asynchrones exposés par les API qui s’efforcent de les fournir.

La réponse à "wait" n'attend pas la fin de l'appel comporte plusieurs explications plus détaillées de ces mots clés.

Pendant ce temps, les conseils de @Stephen Cleary sur async void sont valables. D'autres bonnes explications peuvent être trouvées sur http://www.tonicodes.net/blog/why-you-should-almost-never-write-void-asynchronous-methods/ et https://jaylee.org/archive/2012/07/08/c-sharp-async-tips-and-tricks-part-2-async-void.html

200
Richard Cook

Meilleure solution pour attendre AsynMethod jusqu'à la fin de la tâche est

var result = Task.Run(async() => { return await yourAsyncMethod(); }).Result;
58
Ram chittala