Estoy escribiendo una aplicación WinForms que transfiere datos a un dispositivo de clase USB HID. Mi aplicación utiliza la excelente biblioteca genérica HID v6.0 que se puede encontrar aquí . En pocas palabras, cuando necesito escribir datos en el dispositivo, este es el código que se llama:
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();
}
}
Cuando mi código cae del ciclo while, necesito leer algunos datos del dispositivo. Sin embargo, el dispositivo no puede responder de inmediato, por lo que debo esperar a que vuelva esta llamada antes de continuar. Como existe actualmente, RequestToGetInputReport () se declara así:
private async void RequestToGetInputReport()
{
// lots of code prior to this
int bytesRead = await GetInputReportViaInterruptTransfer();
}
Para lo que vale, la declaración para GetInputReportViaInterruptTransfer () se ve así:
internal async Task<int> GetInputReportViaInterruptTransfer()
Desafortunadamente, no estoy muy familiarizado con el funcionamiento de las nuevas tecnologías async / await en .NET 4.5. Leí un poco antes sobre la palabra clave de espera y eso me dio la impresión de que la llamada a GetInputReportViaInterruptTransfer () dentro de RequestToGetInputReport () esperaría (¿y tal vez sí?) Pero no parece la llamada a RequestToGetInputReport () en sí está esperando porque parece que estoy volviendo a entrar en el ciclo while casi de inmediato?
¿Alguien puede aclarar el comportamiento que estoy viendo?
fuente
void
aTask
lo que había dicho.GetAwaiter().GetResult()
Task
representa la ejecución del método, por loreturn
que se colocan valores y se colocanTask.Result
excepcionesTask.Exception
. Convoid
, el compilador no tiene ningún lugar para colocar excepciones, por lo que simplemente se vuelven a generar en un subproceso de grupo de subprocesos.Lo más importante que debe saber
async
yawait
es queawait
no espera a que se complete la llamada asociada. Lo queawait
hace es devolver el resultado de la operación de forma inmediata y sincrónica si la operación ya se ha completado o, si no lo ha hecho, programar una continuación para ejecutar el resto delasync
método y luego devolver el control a la persona que llama. Cuando finalice la operación asincrónica, se ejecutará la finalización programada.La respuesta a la pregunta específica en el título de su pregunta es bloquear el
async
valor de retorno de un método (que debe ser de tipoTask
oTask<T>
) llamando a unWait
método apropiado :En este fragmento de código,
CallGetFooAsyncAndWaitOnResult
es un contenedor sincrónico alrededor del método asincrónicoGetFooAsync
. Sin embargo, este patrón debe evitarse en su mayor parte, ya que bloqueará un subproceso de grupo de subprocesos completo durante la operación asincrónica. Este es un uso ineficiente de los diversos mecanismos asincrónicos expuestos por las API que hacen grandes esfuerzos para proporcionarlos.La respuesta en "esperar" no espera a que se complete la llamada, tiene varias explicaciones más detalladas de estas palabras clave.
Mientras tanto, la orientación de @Stephen Cleary sobre las
async void
retenciones. Otras buenas explicaciones de por qué se pueden encontrar en http://www.tonicodes.net/blog/why-you-should-almost-never-write-void-asynchronous-methods/ y https://jaylee.org/archive/ 2012/07/08 / c-sharp-async-tips-and-tricks-part-2-async-void.htmlfuente
await
como una "espera asincrónica", es decir, bloquea el método (si es necesario) pero no el hilo . Por lo tanto, tiene sentido hablar sobreRequestToSendOutputReport
"esperar"RequestToGetInputReport
aunque no sea una espera de bloqueo .La mejor solución para esperar AsynMethod hasta completar la tarea es
fuente
Aquí hay una solución alternativa usando una bandera:
fuente
simplemente ponga Wait () para esperar hasta que se complete la tarea
GetInputReportViaInterruptTransfer().Wait();
fuente
En realidad, esto me pareció más útil para las funciones que devuelven IAsyncAction.
fuente
El siguiente fragmento muestra una forma de garantizar que se complete el método esperado antes de volver a la persona que llama. SIN EMBARGO, no diría que es una buena práctica. Edite mi respuesta con explicaciones si piensa lo contrario.
fuente
await
+ theConsole.WriteLine
es convirtiéndose en aTask
, lo que cede el control entre los dos. por lo que su 'solución' finalmente generará unaTask<T>
, que no resuelve el problema. Hacer unTask.Wait
testamento en realidad detendrá el procesamiento (con posibilidades de punto muerto, etc.). En otras palabras, enawait
realidad no espera, simplemente combina dos partes ejecutables asincrónicamente en una solaTask
(que alguien puede mirar o esperar)