Tengo una API central de .net que tiene un controlador que crea un objeto agregado para devolver. el objeto que crea está hecho de datos que provienen de 3 llamadas de método a una clase de servicio. Estos son todos independientes entre sí y se pueden ejecutar de forma aislada entre sí. Actualmente estoy usando tareas para mejorar el rendimiento de este controlador. la versión actual se parece a esto...
[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 usar tareas es la mejor solución aquí. ¿Usar async/await sería una mejor idea y una forma más aceptada?
Por ejemplo, ¿debería marcarse el controlador como asíncrono y esperar en cada llamada a los métodos de servicio?
[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); }TaskWhenAll para devolver el objeto de tarea en espera. Por lo tanto, con el método asíncrono puede esperar tareas en lugar de bloquear el hilo.Task<T> para devolver resultados del tipo requerido.Task<TResult>.RunGetnull para devolver las propiedades del objeto si algunas de las tareas no se completan correctamente. Puede usar otro enfoque, por ejemplo, devolver un error si algunas de las tareas fallaron.Según tengo entendido, desea que esto se ejecute en paralelo, por lo que no creo que haya ningún problema con su código. Como mencionó Gabriel, podrías esperar a que terminen las tareas.
[HttpGet] public async Task<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 puede usar los resultados de las tareas para guardar algunas líneas de códigos y hacer que el código en general sea "mejor" (ver comentarios):
[HttpGet] public async Task<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); }Esto funciona bien, sin embargo, me pregunto si usar tareas es la mejor solución aquí. ¿Usar async/await sería una mejor idea y una forma más aceptada?
Si, absolutamente. Hacer un procesamiento paralelo en ASP.NET consume múltiples subprocesos por solicitud, lo que puede afectar gravemente su escalabilidad. El procesamiento asíncrono es muy superior para E/S.
Para usar 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; cambie eso para usar llamadas HTTP asincrónicas (por ejemplo, HttpClient ). Luego, deje que async crezca naturalmente desde allí.
Eventualmente, terminará con métodos asíncronos getdata1Async , getdata2Async y getdata3Async , que se pueden consumir simultáneamente 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 curso, myControllerAction usa cero subprocesos en lugar de cuatro .