J'écris une application WinForms qui transfère des données vers un périphérique de classe USB HID. Mon application utilise l'excellente bibliothèque Generic HID v6.0 qui peut être trouvée ici . En un mot, lorsque j'ai besoin d'écrire des données sur l'appareil, c'est le code qui est appelé:
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 sort de la boucle while, je dois lire certaines données de l'appareil. Cependant, l'appareil ne peut pas répondre tout de suite. Je dois donc attendre le retour de cet appel avant de continuer. Tel qu'il existe actuellement, 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 suis pas très familier avec le fonctionnement des nouvelles technologies async / await dans .NET 4.5. J'ai fait une petite lecture plus tôt sur le mot-clé await et cela m'a donné l'impression que l'appel à GetInputReportViaInterruptTransfer () à l'intérieur de RequestToGetInputReport () attendrait (et peut-être qu'il le fait?) Mais cela ne ressemble pas à 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?
la source
void
àTask
tout comme vous l' aviez dit.GetAwaiter().GetResult()
Task
représente l'exécution de la méthode - lesreturn
valeurs sont donc placéesTask.Result
et les exceptions sont placéesTask.Exception
. Avecvoid
, le compilateur n'a nulle part où placer les exceptions, elles sont donc simplement relancées sur un thread de pool de threads.La chose la plus importante à savoir
async
etawait
c'est queawait
cela n'attend pas la fin de l'appel associé. Ce quiawait
fait est de 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, de planifier une continuation pour exécuter le reste de laasync
méthode, puis de renvoyer le contrôle à l'appelant. Une fois l'opération asynchrone terminée, l'achèvement planifié s'exécutera.La réponse à la question spécifique dans le titre de votre question est de bloquer
async
la valeur de retour d' une méthode (qui doit être de typeTask
ouTask<T>
) en appelant uneWait
méthode appropriée :Dans cet extrait de code, se
CallGetFooAsyncAndWaitOnResult
trouve un wrapper synchrone autour de la méthode asynchroneGetFooAsync
. Cependant, ce modèle doit être évité pour la plupart car il bloquera un thread de pool de threads entier pendant 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 déploient de grands efforts pour les fournir.La réponse à "attendre" n'attend pas la fin de l'appel a plusieurs explications plus détaillées de ces mots-clés.
Pendant ce temps, les conseils de @Stephen Cleary sur les
async void
prises. D'autres explications intéressantes sur pourquoi 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-trucs-et-astuces-partie-2-async-void.htmlla source
await
comme d'une "attente asynchrone" - c'est-à-dire que cela bloque la méthode (si nécessaire) mais pas le thread . Il est donc logique de parler d 'RequestToSendOutputReport
«attente»RequestToGetInputReport
même si ce n'est pas une attente bloquante .La meilleure solution pour attendre AsynMethod jusqu'à ce que la tâche soit terminée est
la source
Voici une solution de contournement utilisant un indicateur:
la source
il suffit de mettre Wait () pour attendre la fin de la tâche
GetInputReportViaInterruptTransfer().Wait();
la source
L'extrait de code suivant montre un moyen de s'assurer que la méthode attendue se termine avant de retourner à l'appelant. CEPENDANT, je ne dirais pas que c'est une bonne pratique. Veuillez modifier ma réponse avec des explications si vous pensez le contraire.
la source
await
+ leConsole.WriteLine
est de devenir unTask
, ce qui abandonne le contrôle entre les deux. ainsi votre «solution» produira finalement unTask<T>
, qui ne résout pas le problème. Faire unTask.Wait
testament arrêtera en fait le traitement (avec des possibilités de blocage, etc.). En d'autres termes,await
n'attend pas réellement, il combine simplement deux parties exécutables de manière asynchrone en une seuleTask
(que quelqu'un peut regarder ou attendre)En fait, j'ai trouvé cela plus utile pour les fonctions qui retournent IAsyncAction.
la source