c# - whenall - ¿Cómo esperar para que se complete el método async?
task whenall c# (4)
Estoy escribiendo una aplicación WinForms que transfiere datos a un dispositivo de clase USB HID. Mi aplicación utiliza la excelente biblioteca Generic HID v6.0 que se puede encontrar here . 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 abandona el ciclo while, necesito leer algunos datos del dispositivo. Sin embargo, el dispositivo no puede responder de inmediato, así que tengo que esperar a que regrese 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();
}
Por lo que vale, la declaración de GetInputReportViaInterruptTransfer () tiene este aspecto:
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 await y eso me dio la impresión de que la llamada a GetInputReportViaInterruptTransfer () dentro de RequestToGetInputReport () esperaría (¿y quizás sí?) Pero no parece la llamada a RequestToGetInputReport () está esperando porque parece que estoy volviendo a ingresar al ciclo while casi de inmediato?
¿Alguien puede aclarar el comportamiento que estoy viendo?
El siguiente fragmento muestra una forma de asegurarse de que el método esperado se complete antes de regresar a la persona que llama. SIN EMBARGO, no diría que es una buena práctica. Por favor edita mi respuesta con explicaciones si piensas lo contrario.
public async Task AnAsyncMethodThatCompletes()
{
await SomeAsyncMethod();
DoSomeMoreStuff();
await Task.Factory.StartNew(() => { }); // <-- This line here, at the end
}
await AnAsyncMethodThatCompletes();
Console.WriteLine("AnAsyncMethodThatCompletes() completed.")
Evita el async void
. Haga que sus métodos devuelvan Task
lugar de void
. Entonces puedes await
.
Me gusta esto:
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 mejor solución para esperar AsynMethod hasta completar la tarea es
var result = Task.Run(async() => { return await yourAsyncMethod(); }).Result;
Lo más importante que debe saber acerca de async
y await
es que await
no espera a que finalice la llamada asociada. Lo que await
hacer 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 del método async
y luego devolver el control a la persona que llama. Cuando la operación asíncrona se complete, la finalización programada se ejecutará.
La respuesta a la pregunta específica en el título de su pregunta es bloquear el valor de retorno de un método async
(que debe ser de tipo Task
o Task<T>
) llamando a un método de Wait
apropiado:
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;
}
En este fragmento de código, CallGetFooAsyncAndWaitOnResult
es un contenedor síncrono alrededor del método asincrónico GetFooAsync
. Sin embargo, este patrón se debe evitar en su mayor parte, ya que bloqueará un subproceso completo del grupo de subprocesos durante la operación asincrónica. Este es un uso ineficiente de los diversos mecanismos asíncronos expuestos por las API que hacen grandes esfuerzos para proporcionarlos.
La respuesta en "esperar" no espera a que la finalización de la llamada tenga varias explicaciones más detalladas de estas palabras clave.
Mientras tanto, la orientación de @Stephen Cleary sobre el async void
cumple. 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 http://www.jaylee.org/post/2012/07/08/c-sharp-async-tips-and-tricks-part-2-async-void.aspx .