without run method espaƱol ejemplos await asynccontext async c# .net asp.net-web-api asp.net-core

run - call async method c#



Usando async/await o task en el controlador web api(.net core) (3)

Tengo una API .net core que tiene un controlador que crea un objeto agregado para devolver. el objeto que crea está hecho de datos que provienen de 3 llamadas a un método a una clase de servicio. Todos estos son independientes entre sí y se pueden ejecutar de forma aislada unos de otros. Actualmente estoy usando tareas para mejorar el rendimiento de este controlador. la versión actual se ve más o menos así ...

[HttpGet] public IActionResult myControllerAction() { var data1 = new sometype1(); var data2 = new sometype2(); var data3 = new List<sometype3>(); var t1 = new Task(() => { data1 = service.getdata1(); }); t1.Start(); var t2 = new Task(() => { data2 = service.getdata2(); }); t2.Start(); var t3 = new Task(() => { data3 = service.getdata2(); }); t3.Start(); Task.WaitAll(t1, t2, t3); var data = new returnObject { d1 = data1, d2 = data2, d2 = data3 }; return Ok(data); }

Esto funciona bien, sin embargo, me pregunto si el uso de tareas es la mejor solución aquí. ¿Usaría async / await una mejor idea y una forma más aceptada?

Por ejemplo, ¿debería marcarse el controlador como asíncrono y esperar a cada llamada a los métodos de servicio?


Esto funciona bien, sin embargo, me pregunto si el uso de tareas es la mejor solución aquí. ¿Usaría async / await una mejor idea y una forma más aceptada?

Si, absolutamente. Hacer un procesamiento paralelo en ASP.NET consume múltiples hilos por solicitud, lo que puede afectar gravemente su escalabilidad. El procesamiento asincrónico es muy superior para E / S.

Para usar la función async , primero comience con su llamada de nivel más bajo, en algún lugar dentro de su servicio. Probablemente esté haciendo una llamada HTTP en algún momento; cambiar eso para usar llamadas HTTP asincrónicas (por ejemplo, HttpClient ). Entonces deja que async crezca naturalmente a partir de ahí.

Eventualmente, terminará con los getdata1Async asynchronous getdata1Async , getdata2Async y getdata3Async , que se pueden consumir al mismo tiempo como tales:

[HttpGet] public async Task<IActionResult> myControllerAction() { var t1 = service.getdata1Async(); var t2 = service.getdata2Async(); var t3 = service.getdata3Async(); await Task.WhenAll(t1, t2, t3); var data = new returnObject { d1 = await t1, d2 = await t2, d3 = await t3 }; return Ok(data); }

Con este enfoque, mientras las tres llamadas de servicio están en progreso, myControllerAction usa cero hilos en lugar de cuatro .


Según tengo entendido, quiere que esto se ejecute en paralelo, por lo que no creo que haya ningún problema con su código. Como Gabriel mencionó, podrías esperar el final de las tareas.

[HttpGet] public async IActionResult myControllerAction() { var data1 = new sometype1(); var data2 = new sometype2(); var data3 = new List<sometype3>(); var t1 = Task.Run(() => { data1 = service.getdata1(); }); var t2 = Task.Run(() => { data2 = service.getdata2(); }); var t3 = Task.Run(() => { data3 = service.getdata3(); }); await Task.WhenAll(t1, t2, t3); // otherwise a thread will be blocked here var data = new returnObject { d1 = data1, d2 = data2, d2 = data3 }; return Ok(data); }

También podría usar los resultados de las tareas para guardar algunas líneas de códigos y hacer que el código general sea "mejor" (ver comentarios):

[HttpGet] public async IActionResult myControllerAction() { var t1 = Task.Run(() => service.getdata1() ); var t2 = Task.Run(() => service.getdata2() ); var t3 = Task.Run(() => service.getdata3() ); await Task.WhenAll(t1, t2, t3); // otherwise a thread will be blocked here var data = new returnObject { d1 = t1.Result, d2 = t2.Result, d2 = t3.Result }; return Ok(data); }


[HttpGet] public async Task<IActionResult> GetAsync() { var t1 = Task.Run(() => service.getdata1()); var t2 = Task.Run(() => service.getdata2()); var t3 = Task.Run(() => service.getdata3()); await Task.WhenAll(t1, t2, t3); var data = new returnObject { d1 = t1.Status == TaskStatus.RanToCompletion ? t1.Result : null, d2 = t2.Status == TaskStatus.RanToCompletion ? t2.Result : null, d3 = t3.Status == TaskStatus.RanToCompletion ? t3.Result : null }; return Ok(data); }

  1. Su hilo de acción actualmente está bloqueado cuando está esperando las tareas. Use TaskWhenAll para devolver el objeto Tarea agitable. Por lo tanto, con el método async puede esperar para las tareas en lugar de bloquear el hilo.
  2. En lugar de crear variables locales y asignarlas en tareas, puede usar la Task<T> para devolver los resultados del tipo requerido.
  3. En lugar de crear y ejecutar tareas, use Task<TResult>.Run método Task<TResult>.Run
  4. Recomiendo usar la convención para nombres de acción: si la acción acepta la solicitud GET, su nombre debería comenzar con Get
  5. A continuación, debe verificar si las tareas se completaron correctamente. Se realiza al verificar el estado de la tarea. En mi ejemplo, utilicé valores null para las propiedades del objeto de retorno si algunas de las tareas no se completan satisfactoriamente. Puede usar otro enfoque, por ejemplo, devolver error si algunas de las tareas fallaron.