Nuestros mejores programadores han agotado sus provisiones de café, investigando diariamente por la respuesta, hasta que Víctor halló el hallazgo en GitLab así que en este momento la compartimos aquí.
Solución:
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.
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
). Entonces deja async
crecer naturalmente desde allí.
Eventualmente, terminará con asincrónico getdata1Async
, getdata2Async
y getdata3Async
métodos, que se pueden consumir simultáneamente como tales:
[HttpGet]
public async Task 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
usos cero hilos en lugar de cuatro.
[HttpGet]
public async Task 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);
- Su hilo de acción está actualmente bloqueado cuando está esperando tareas. Utilizar
TaskWhenAll
para devolver el objeto de tarea esperable. Por lo tanto, con el método asíncrono puede esperar tareas en lugar de bloquear el hilo. - En lugar de crear variables locales y asignarlas en tareas, puede usar
Task
para devolver resultados del tipo requerido. - En lugar de crear y ejecutar tareas, utilice
Task
método.Run - Recomiendo usar la convención para los nombres de acción: si la acción acepta la solicitud GET, su nombre debe comenzar con
Get
- A continuación, debe verificar si las tareas se completaron con éxito. Se realiza comprobando el estado de la tarea. En mi muestra usé
null
valores para las propiedades del objeto de retorno 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 myControllerAction()
var data1 = new sometype1();
var data2 = new sometype2();
var data3 = new List();
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 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);
Comentarios y calificaciones
Si conservas alguna desconfianza o capacidad de refinar nuestro división te sugerimos escribir una disquisición y con gusto lo ojearemos.