Ich schreibe eine WinForms-Anwendung, die Daten auf ein Gerät der USB-HID-Klasse überträgt. Meine Anwendung verwendet die hervorragende generische HID-Bibliothek v6.0, die hier zu finden ist . Kurz gesagt, wenn ich Daten auf das Gerät schreiben muss, wird folgender Code aufgerufen:
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();
}
}
Wenn mein Code aus der while-Schleife herausfällt, muss ich einige Daten vom Gerät lesen. Das Gerät kann jedoch nicht sofort antworten, daher muss ich warten, bis dieser Anruf zurückkommt, bevor ich fortfahre. RequestToGetInputReport () wird wie folgt deklariert:
private async void RequestToGetInputReport()
{
// lots of code prior to this
int bytesRead = await GetInputReportViaInterruptTransfer();
}
Für das, was es wert ist, sieht die Deklaration für GetInputReportViaInterruptTransfer () folgendermaßen aus:
internal async Task<int> GetInputReportViaInterruptTransfer()
Leider bin ich mit der Funktionsweise der neuen Async / Wait-Technologien in .NET 4.5 nicht sehr vertraut. Ich habe vorhin ein wenig über das Schlüsselwort await gelesen und dadurch den Eindruck erweckt, dass der Aufruf von GetInputReportViaInterruptTransfer () in RequestToGetInputReport () warten würde (und vielleicht auch?), Aber es scheint nicht so, als würde RequestToGetInputReport () aufgerufen selbst wartet, weil ich fast sofort wieder in die while-Schleife eintrete?
Kann jemand das Verhalten klären, das ich sehe?
quelle
void
zu dem zu wechseln ,Task
was Sie gesagt hatten.GetAwaiter().GetResult()
Task
stellt die Ausführung der Methode dar - also werdenreturn
WerteTask.Result
und Ausnahmen gesetztTask.Exception
. Mitvoid
kann der Compiler keine Ausnahmen platzieren, sodass sie nur in einem Thread-Pool-Thread erneut ausgelöst werden.Die wichtigste Sache zu wissen
async
undawait
ist , dassawait
nicht der Fall ist für den damit verbundenen Aufruf vollständig zu warten. Sieawait
müssen das Ergebnis der Operation sofort und synchron zurückgeben, wenn die Operation bereits abgeschlossen ist, oder, falls dies nicht der Fall ist, eine Fortsetzung planen, um den Rest derasync
Methode auszuführen , und dann die Steuerung an den Aufrufer zurückgeben. Wenn der asynchrone Vorgang abgeschlossen ist, wird der geplante Abschluss ausgeführt.Die Antwort auf die spezifische Frage im Titel Ihrer Frage besteht darin
async
, den Rückgabewert einer Methode (der vom TypTask
oder sein sollteTask<T>
) durch Aufrufen einer geeignetenWait
Methode zu blockieren :In diesem Code-Snippet
CallGetFooAsyncAndWaitOnResult
handelt es sich um einen synchronen Wrapper um eine asynchrone MethodeGetFooAsync
. Dieses Muster ist jedoch größtenteils zu vermeiden, da es einen gesamten Thread-Pool-Thread für die Dauer der asynchronen Operation blockiert. Dies ist eine ineffiziente Verwendung der verschiedenen asynchronen Mechanismen, die von APIs bereitgestellt werden, die große Anstrengungen unternehmen, um sie bereitzustellen.Die Antwort unter "Warten" wartet nicht auf den Abschluss des Anrufs. Sie enthält mehrere detailliertere Erklärungen zu diesen Schlüsselwörtern.
In der Zwischenzeit
async void
hält @Stephen Clearys Anleitung zu . Weitere nette Erklärungen dafür finden Sie unter http://www.tonicodes.net/blog/why-you-should-almost-never-write-void-asynchronous-methods/ und https://jaylee.org/archive/ 2012/07/08 / c-scharf-asynchrone-Tipps-und-Tricks-Teil-2-asynchrone-void.htmlquelle
await
ein "asynchrones Warten" nachzudenken (und darüber zu sprechen) - das heißt, es blockiert die Methode (falls erforderlich), aber nicht den Thread . Es ist also sinnvoll, überRequestToSendOutputReport
"Warten auf" zu sprechenRequestToGetInputReport
, obwohl es kein blockierendes Warten ist.Die beste Lösung, um AsynMethod zu warten, bis die Aufgabe abgeschlossen ist, ist
quelle
Hier ist eine Problemumgehung mit einem Flag:
quelle
Setzen Sie einfach Wait (), um zu warten, bis die Aufgabe abgeschlossen ist
GetInputReportViaInterruptTransfer().Wait();
quelle
Eigentlich fand ich dies hilfreicher für Funktionen, die IAsyncAction zurückgeben.
quelle
Das folgende Snippet zeigt eine Möglichkeit, um sicherzustellen, dass die erwartete Methode abgeschlossen ist, bevor Sie zum Aufrufer zurückkehren. Ich würde jedoch nicht sagen, dass es eine gute Praxis ist. Bitte bearbeiten Sie meine Antwort mit Erklärungen, wenn Sie anders denken.
quelle
await
+ das zu tun,Console.WriteLine
darin besteht, ein zu werdenTask
, was die Kontrolle zwischen den beiden aufgibt. Ihre "Lösung" ergibt also letztendlich eineTask<T>
, die das Problem nicht angeht. Wenn Sie einTask.Wait
Testament ausführen , wird die Verarbeitung tatsächlich gestoppt (mit Deadlock-Möglichkeiten usw.). Mit anderen Worten,await
wartet nicht wirklich, es kombiniert einfach zwei asynchron ausführbare Teile zu einem einzigenTask
(auf den jemand schauen oder warten kann)