Nota: Resulta que este problema es específico de Unity.
Leí que se debía evitar el async void . Estoy tratando de hacerlo usando Result , pero mi aplicación sigue bloqueándose. ¿Cómo puedo evitar usar async void?
public async void PrintNumberWithAwait() { int number = await GetNumber(); Debug.Log(number); //Successfully prints "5" } public void PrintNumberWithResult() { int number = GetNumber().Result; Debug.Log(number); //Application Freezes } private async Task<int> GetNumber() { await Task.Delay(1000); return 5; } Pensé que esto era correcto, pero debo estar perdiendo algo. ¿Cómo uso async/await sin tener async void ?
Ejecuté mis pruebas por separado con el siguiente código (comenté uno a la vez):
PrintNumberWithAwait(); PrintNumberWithResult();No entendiste lo que significa el async void que se debe evitar.
No significa que nunca debas usar una tarea sin resultados adjuntos. Simplemente dice que los métodos asincrónicos que los invocan deben devolver una Task , no una void .
Simplemente tome la firma de su método asíncrono de
public async void PrintNumberWithAwait() y reemplace void con Task
public async Task PrintNumberWithAwait() { int number = await GetNumber(); Debug.Log(number); //Successfully prints "5" }Ahora los métodos de llamada tienen la opción de esperar el resultado, cuando y si así lo desean. Cualquiera:
await PrintNumberWithAwait();O
Task t = PrintNumberWithAwait(); // Do other stuff // ... await t;Version corta
El contexto de sincronización de Unity es de un solo subproceso. Entonces:
Versión detallada
Dijiste que estás usando Unity. Unity es "99%" un marco de un solo subproceso. Así que supongo que este código se ejecuta en Main, UI Thread.
Pasemos a lo que hace su código en detalle, al ejecutar PrintNumberWithResult().
Solución No llamar nunca .Resultado.
Si desea disparar y olvidar una operación de tarea, use Task.Run(() => ). ¡Pero espera! ¡Esa no es una buena idea en Unity! (Tal vez esté en otras aplicaciones .NET).
Si usa Task.Run() en Unity, está forzando la ejecución del código asíncrono usando el contexto de sincronización de .NET predeterminado, que usa un grupo de subprocesos, y eso podría causar algunos problemas de sincronización si está llamando a alguna API relacionada con Unity.
Lo que desea hacer en ese caso es usar un vacío asíncrono (no realmente por razones relacionadas con el manejo de excepciones), un método de tarea asíncrona que nunca esperará (mejor), o tal vez usar una biblioteca como UniTask para espera asíncrona usando Unity (Lo mejor en mi opinión).