Tengo pocos servicios REST asíncronos que no dependen unos de otros. Es decir, mientras "espero" una respuesta del Servicio1, puedo llamar al Servicio2, al Servicio3, etc.
Por ejemplo, consulte el siguiente código:
var service1Response = await HttpService1Async();
var service2Response = await HttpService2Async();
// Use service1Response and service2Response
Ahora, service2Response
no depende service1Response
y se pueden obtener de forma independiente. Por lo tanto, no es necesario que espere la respuesta del primer servicio para llamar al segundo servicio.
No creo que pueda usar Parallel.ForEach
aquí, ya que no es una operación vinculada a la CPU.
Para llamar a estas dos operaciones en paralelo, ¿puedo llamar al uso Task.WhenAll
? Un problema que veo usando Task.WhenAll
es que no devuelve resultados. Para obtener el resultado, ¿puedo llamar task.Result
después de llamar Task.WhenAll
, ya que todas las tareas ya se han completado y todo lo que necesito para obtener una respuesta?
Código de muestra:
var task1 = HttpService1Async();
var task2 = HttpService2Async();
await Task.WhenAll(task1, task2)
var result1 = task1.Result;
var result2 = task2.Result;
// Use result1 and result2
¿Es este código mejor que el primero en términos de rendimiento? ¿Algún otro enfoque que pueda usar?
fuente
I do not think I can use Parallel.ForEach here since it is not CPU bound operation
- No veo la lógica allí. La concurrencia es concurrencia.Parallel.ForEach
generaría nuevos hilos mientrasasync await
que haría todo en un solo hilo.await
) antes de que esté listo.WhenAll
antes de hacerloResult
con la idea de que completa todas las tareas antes de que se llame a .Result. Dado que Task.Result bloquea el hilo de llamada, supongo que si lo llamo después de que las tareas se hayan completado, devolverá el resultado inmediatamente. Quiero validar la comprensión.Respuestas:
Pero sí devuelve los resultados. Todos estarán en una matriz de un tipo común, por lo que no siempre es útil usar los resultados ya que necesita encontrar el elemento en la matriz que corresponde al
Task
resultado para el que desea el resultado, y potencialmente convertirlo en su tipo real, por lo que podría no ser el enfoque más fácil / más legible en este contexto, pero cuando solo desea obtener todos los resultados de cada tarea, y el tipo común es el tipo con el que desea tratarlos, entonces es genial .Sí, puedes hacer eso. También podría
await
usarlos (await
desenvolvería la excepción en cualquier tarea fallida, mientrasResult
que lanzaría una excepción agregada, pero de lo contrario sería lo mismo).Realiza las dos operaciones al mismo tiempo, en lugar de una y luego la otra. Que eso sea mejor o peor depende de cuáles sean esas operaciones subyacentes. Si las operaciones subyacentes son "leer un archivo del disco", entonces hacerlas en paralelo probablemente sea más lento, ya que solo hay un cabezal de disco y solo puede estar en un lugar en un momento dado; saltar entre dos archivos será más lento que leer un archivo y luego otro. Por otro lado, si las operaciones son "realizar alguna solicitud de red" (como es el caso aquí), entonces es muy probable que sean más rápidas (al menos hasta un cierto número de solicitudes concurrentes), porque puede esperar una respuesta desde otra computadora de red igual de rápido cuando también hay otra solicitud de red pendiente en curso. Si quieres saber si es así
Si no es importante para usted que conozca todas las excepciones lanzadas entre todas las operaciones que está haciendo en paralelo en lugar de solo la primera, simplemente puede realizar
await
las tareas sinWhenAll
ninguna. Lo único queWhenAll
te da es tener unaAggregateException
excepción con cada una de las tareas con fallas, en lugar de tirar cuando llegas a la primera tarea con fallas. Es tan simple como:fuente
Aquí está el método de extensión que hace uso de SemaphoreSlim y permite establecer el grado máximo de paralelismo
Uso de muestra:
fuente
Puedes usar
o
fuente