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?
É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();
}
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
Meilleure solution pour attendre AsynMethod jusqu'à la fin de la tâche est
var result = Task.Run(async() => { return await yourAsyncMethod(); }).Result;