Respuestas:
La mejor práctica es marcar la función async void
solo si es el método de disparo y olvido, si desea esperar, debe marcarlo como async Task
.
En caso de que aún quieras esperar, envuélvelo así await Task.Run(() => blah())
await Task.Run(() => An_async_void_method_I_can_not_modify_now())
await Task.Run(() => blah())
es engañosa. Esto no espera la finalización de la función asíncrona blah
, solo espera la creación (trivial) de la tarea y continúa inmediatamente antes de que se blah()
haya completado.
Thread.Sleep
no es asíncrono. Esta pregunta se trata de esperar una async void
función, por ejemploasync void blah() { Task.Delay(10000); }
La mejor solución es usar async Task
. Debe evitar async void
por varias razones, una de las cuales es la componibilidad.
Si no se puede hacer que el método regrese Task
(p. Ej., Es un controlador de eventos), puede usar SemaphoreSlim
para que la señal del método esté a punto de salir. Considera hacer esto en un finally
bloque.
haga un AutoResetEvent, llame a la función, luego espere en AutoResetEvent y luego configúrelo dentro de vacío asíncrono cuando sepa que está hecho.
También puede esperar una Tarea que regrese de su vacío asíncrono
Realmente no necesita hacer nada manualmente, la await
palabra clave detiene la ejecución de la función hasta que blah()
regrese.
private async void SomeFunction()
{
var x = await LoadBlahBlah(); <- Function is not paused
//rest of the code get's executed even if LoadBlahBlah() is still executing
}
private async Task<T> LoadBlahBlah()
{
await DoStuff(); <- function is paused
await DoMoreStuff();
}
T
es el tipo de objeto blah()
devuelve
Realmente no puede await
una void
función, así LoadBlahBlah()
que no puede servoid
LoadBlahBlah()
que termine, noblah()
Sé que esta es una vieja pregunta, pero aún sigo siendo un problema, y todavía no hay una solución clara para hacerlo correctamente cuando se usa async / wait en un método de firma vacío async.
Sin embargo, noté que .Wait () funciona correctamente dentro del método void.
y como async void y void tienen la misma firma, es posible que deba hacer lo siguiente.
void LoadBlahBlah()
{
blah().Wait(); //this blocks
}
Lo suficientemente confuso como async / await no se bloquea en el siguiente código.
async void LoadBlahBlah()
{
await blah(); //this does not block
}
Cuando descompila su código, supongo que el vacío asíncrono crea una Tarea interna (al igual que la Tarea asíncrona), pero dado que la firma no admite devolver esas Tareas internas
Esto significa que internamente el método vacío asíncrono aún podrá "esperar" los métodos asíncronos internos. pero externamente no puede saber cuándo se completa la tarea interna.
Entonces, mi conclusión es que el vacío asíncrono funciona según lo previsto, y si necesita comentarios de la Tarea interna, entonces debe usar la firma de la Tarea asíncrona en su lugar.
espero que mis divagaciones tengan sentido para cualquiera que también busque respuestas.
Editar: hice un código de ejemplo y lo descompuse para ver qué está pasando realmente.
static async void Test()
{
await Task.Delay(5000);
}
static async Task TestAsync()
{
await Task.Delay(5000);
}
Se convierte en (editar: sé que el código del cuerpo no está aquí sino en las máquinas de estado, pero las máquinas de estado eran básicamente idénticas, así que no me molesté en agregarlas)
private static void Test()
{
<Test>d__1 stateMachine = new <Test>d__1();
stateMachine.<>t__builder = AsyncVoidMethodBuilder.Create();
stateMachine.<>1__state = -1;
AsyncVoidMethodBuilder <>t__builder = stateMachine.<>t__builder;
<>t__builder.Start(ref stateMachine);
}
private static Task TestAsync()
{
<TestAsync>d__2 stateMachine = new <TestAsync>d__2();
stateMachine.<>t__builder = AsyncTaskMethodBuilder.Create();
stateMachine.<>1__state = -1;
AsyncTaskMethodBuilder <>t__builder = stateMachine.<>t__builder;
<>t__builder.Start(ref stateMachine);
return stateMachine.<>t__builder.Task;
}
ni AsyncVoidMethodBuilder ni AsyncTaskMethodBuilder tienen realmente ningún código en el método Start que les sugiera que bloqueen y siempre se ejecuten de forma asíncrona después de que se inicien.
lo que significa que sin la Tarea de retorno, no habría forma de verificar si está completa.
como se esperaba, solo inicia la Tarea ejecutándose de forma asíncrona y luego continúa en el código. y la Tarea asíncrona, primero comienza la Tarea y luego la devuelve.
así que supongo que mi respuesta sería nunca usar el vacío asíncrono, si necesita saber cuándo se realiza la tarea, para eso está la tarea asíncrona.