Ejecutar métodos asíncronos en paralelo

Ejecutar métodos asíncronos en paralelo

Sí, el "mejor" enfoque es utilizar el Task.WhenAll método. Sin embargo, su segundo enfoque debería haberse ejecutado en paralelo. Creé un .NET Fiddle, esto debería ayudar a arrojar algo de luz. Su segundo enfoque debería ejecutarse en paralelo. ¡Mi violín lo demuestra!

Considere lo siguiente:

public Task<Thing[]> GetThingsAsync()
{
    var first = GetExpensiveThingAsync();
    var second = GetExpensiveThingAsync();

    return Task.WhenAll(first, second);
}

Nota

Se prefiere usar el sufijo "Async", en lugar de GetThings y GetExpensiveThing - deberíamos tener GetThingsAsync y GetExpensiveThingAsync respectivamente - fuente.


Task.WhenAll() tiene una tendencia a perder rendimiento con gran escala/cantidad de tareas que se ejecutan simultáneamente, sin moderación/limitación.

Si está haciendo muchas tareas en una lista y desea esperar el resultado final, le propongo usar un partition con un límite en el grado de paralelismo.

He modificado el enfoque elegante del blog de Stephen Toub para LINQ moderno:

public static Task ParallelForEachAsync<T>(this IEnumerable<T> source, Func<T, Task> funcBody, int maxDoP = 4)
{
    async Task AwaitPartition(IEnumerator<T> partition)
    {
        using (partition)
        {
            while (partition.MoveNext())
            {
                 await Task.Yield(); // prevents a sync/hot thread hangup
                 await funcBody(partition.Current);
            }
        }
    }

    return Task.WhenAll(
        Partitioner
            .Create(source)
            .GetPartitions(maxDoP)
            .AsParallel()
            .Select(p => AwaitPartition(p)));
}

La forma en que funciona es simple, tome un IEnumerable:divídalo en particiones uniformes y dispare una función/método contra cada elemento, en cada partición, al mismo tiempo. No más de un elemento en cada partición a la vez, pero n tareas en n particiones.

Uso de extensión:

await myList.ParallelForEachAsync(myFunc, Environment.ProcessorCount);

Editar:ahora guardo algunas sobrecargas en un repositorio en Github si necesita más opciones. También está en NuGet para NetStandard.

Edición 2:gracias a los comentarios de Theodor a continuación, pude evitar que las tareas asincrónicas mal escritas bloquearan el paralelismo usando await Task.Yield(); .


Puede usar Task.WhenAll, que regresa cuando se completan todas las tareas dependientes

Marque esta pregunta aquí como referencia