Parallel.ForEach vs Async Forloop en operaciones de E / S pesadas

Quiero comparar dos escenarios teóricos. He simplificado los casos a los fines de la pregunta. Pero básicamente es el típico escenario de consumidor productor. (Me estoy centrando en el consumidor).

Tengo un granQueue<string> dataQueue que tengo que transmitir a múltiples clientes.

Entonces, comencemos con el caso más simple:

 class SequentialBlockingCase
 {
    public static Queue<string> DataQueue = new Queue<string>();
    private static List<string> _destinations = new List<string>();

    /// <summary>
    /// Is the main function that is run in its own thread
    /// </summary>
    private static void Run()
    {
        while (true)
        {
            if (DataQueue.Count > 0)
            {
                string data = DataQueue.Dequeue();
                foreach (var destination in _destinations)
                {
                    SendDataToDestination(destination, data);
                }
            }
            else
            {
                Thread.Sleep(1);
            }
        }
    }

    private static void SendDataToDestination(string destination, string data)
    {
        //TODO: Send data using http post, instead simulate the send
        Thread.Sleep(200);
    }
}
}

Ahora esta configuración funciona bien. Se sienta allí y sondea elQueue y cuando hay datos para enviar, los envía a todos los destinos.

Cuestiones:

Si uno de los destinos no está disponible o es lento, afecta a todos los demás destinos.No hace uso de subprocesos múltiples en el caso de ejecución paralela.Bloques para cada transmisión a cada destino.

Así que aquí está mi segundo intento:

 class ParalleBlockingCase
{
    public static Queue<string> DataQueue = new Queue<string>();
    private static List<string> _destinations = new List<string>();

    /// <summary>
    /// Is the main function that is run in its own thread
    /// </summary>
    private static void Run()
    {
        while (true)
        {
            if (DataQueue.Count > 0)
            {
                string data = DataQueue.Dequeue();
                Parallel.ForEach(_destinations, destination =>
                {
                    SendDataToDestination(destination, data);
                });
            }
            else
            {
                Thread.Sleep(1);
            }
        }
    }

    private static void SendDataToDestination(string destination, string data)
    {
        //TODO: Send data using http post
        Thread.Sleep(200);
    }
}

Esta revisión al menos no afecta a los otros destinos si 1 destino es lento o no está disponible.

Sin embargo, este método todavía está bloqueando y no estoy seguro de siParallel.ForEach hace uso del grupo de subprocesos. Tengo entendido que creará un número X de subprocesos / tareas y ejecutará 4 (CPU de 4 núcleos) a la vez. Pero tiene que terminar completamente la tarea 1 antes de que la tarea 5 pueda comenzar.

De ahí mi tercera opción:

class ParalleAsyncCase
{
    public static Queue<string> DataQueue = new Queue<string>();
    private static List<string> _destinations = new List<string> { };

    /// <summary>
    /// Is the main function that is run in its own thread
    /// </summary>
    private static void Run()
    {
        while (true)
        {
            if (DataQueue.Count > 0)
            {
                string data = DataQueue.Dequeue();
                List<Task> tasks = new List<Task>();
                foreach (var destination in _destinations)
                {
                    var task = SendDataToDestination(destination, data);
                    task.Start();
                    tasks.Add(task);
                }

                //Wait for all tasks to complete
                Task.WaitAll(tasks.ToArray());
            }
            else
            {
                Thread.Sleep(1);
            }
        }
    }

    private static async Task SendDataToDestination(string destination, string data)
    {
        //TODO: Send data using http post
        await Task.Delay(200);
    }
}

Ahora, según tengo entendido, esta opción seguirá bloqueándose en el hilo principal enTask.WaitAll(tasks.ToArray()); lo cual está bien porque no quiero que se escape con la creación de tareas más rápido de lo que pueden ejecutarse.

Pero las tareas que se ejecutarán en paralelo deben hacer uso deThreadPool, y todas las X número de tareas deben comenzar a ejecutarse a la vez, no bloquearse o en orden secuencial. (el grupo de subprocesos se intercambiará entre ellos cuando se activen o se activen)awaiting)

Ahora mi pregunta.

¿La opción 3 tiene algún beneficio de rendimiento sobre la opción 2?

Específicamente en un escenario del lado del servidor de mayor rendimiento. En el software específico en el que estoy trabajando ahora. Habría varias instancias de mi caso de uso simple anterior. Es decir, varios consumidores.

Estoy interesado en las diferencias teóricas y las ventajas y desventajas de las dos soluciones, y tal vez incluso una cuarta opción mejor si hay una.

Respuestas a la pregunta(2)

Su respuesta a la pregunta