Estoy usando un cliente API que es completamente asíncrono, es decir, cada operación devuelve Task
o Task<T>
, por ejemplo:
static async Task DoSomething(int siteId, int postId, IBlogClient client)
{
await client.DeletePost(siteId, postId); // call API client
Console.WriteLine("Deleted post {0}.", siteId);
}
Usando los operadores asíncronos / en espera de C # 5, ¿cuál es la forma correcta / más eficiente de iniciar múltiples tareas y esperar a que todas se completen?
int[] ids = new[] { 1, 2, 3, 4, 5 };
Parallel.ForEach(ids, i => DoSomething(1, i, blogClient).Wait());
o:
int[] ids = new[] { 1, 2, 3, 4, 5 };
Task.WaitAll(ids.Select(i => DoSomething(1, i, blogClient)).ToArray());
Dado que el cliente API está utilizando HttpClient internamente, esperaría que esto emita 5 solicitudes HTTP de inmediato, escribiendo en la consola a medida que se completa cada una.
c#
.net
task-parallel-library
async-await
c#-5.0
Ben Foster
fuente
fuente
Respuestas:
Aunque ejecuta las operaciones en paralelo con el código anterior, este código bloquea cada subproceso en el que se ejecuta cada operación. Por ejemplo, si la llamada de red tarda 2 segundos, cada subproceso se cuelga durante 2 segundos sin hacer nada más que esperar.
Por otro lado, el código anterior
WaitAll
también bloquea los hilos y sus hilos no serán libres de procesar ningún otro trabajo hasta que finalice la operación.Enfoque recomendado
Prefiero
WhenAll
que realice sus operaciones de forma asincrónica en paralelo.Para respaldar esto, aquí hay una publicación de blog detallada que detalla todas las alternativas y sus ventajas / desventajas: Cómo y dónde E / S asincrónicas concurrentes con API web ASP.NET
fuente
WaitAll
también bloquea los hilos" - ¿no bloquea solo un hilo, el que llamóWaitAll
?Tenía curiosidad por ver los resultados de los métodos proporcionados en la pregunta, así como la respuesta aceptada, así que lo puse a prueba.
Aquí está el código:
Y el resultado resultante:
fuente
Como la API a la que llama es asíncrona, la
Parallel.ForEach
versión no tiene mucho sentido. No debe usar.Wait
en laWaitAll
versión ya que eso perdería el paralelismo. Otra alternativa si la persona que llama es asíncrona está usandoTask.WhenAll
después de hacerSelect
yToArray
generar la matriz de tareas. Una segunda alternativa es usar Rx 2.0fuente
Puede usar la
Task.WhenAll
función que puede pasar n tareas;Task.WhenAll
devolverá una tarea que se ejecutará hasta completar cuando se completen todas las tareas que pasóTask.WhenAll
. Tienes que esperar asincrónicamenteTask.WhenAll
para que no bloquees tu hilo de la interfaz de usuario:fuente
Parallel.ForEach
requiere una lista de trabajadores definidos por el usuario y una no asíncronaAction
para realizar con cada trabajador.Task.WaitAll
yTask.WhenAll
requieren unList<Task>
, que son por definición asíncronos.La respuesta de RiaanDP me pareció muy útil para entender la diferencia, pero necesita una corrección . No hay suficiente reputación para responder a su comentario, de ahí mi propia respuesta.
Parallel.ForEach
La salida resultante está debajo. Los tiempos de ejecución son comparables. Ejecuté esta prueba mientras mi computadora realizaba el análisis antivirus semanal. Cambiar el orden de las pruebas cambió los tiempos de ejecución en ellas.
fuente