Parallel.ForEach против Async Forloop в тяжелых операциях ввода-вывода

Я хочу сравнить два теоретических сценария. Я упростил случаи для цели вопроса. Но в основном это ваш типичный потребительский сценарий производителя. (Я ориентируюсь на потребителя).

У меня большойQueue<string> dataQueue что я должен передать нескольким клиентам.

Итак, давайте начнем с более простого случая:

 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);
    }
}
}

Теперь эта настройка работает просто отлично. Он сидит там и опрашиваетQueue и когда есть данные для отправки, он отправляет их всем адресатам.

Вопросы:

Если один из пунктов назначения недоступен или работает медленно, он влияет на все остальные пункты назначения.Он не использует многопоточность в случае параллельного выполнения.Блоки для каждой передачи в каждый пункт назначения.

Итак, вот моя вторая попытка:

 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);
    }
}

Эта ревизия, по крайней мере, не влияет на другие пункты назначения, если 1 пункт назначения медленный или недоступен.

Однако этот метод все еще блокирует, и я не уверен, еслиParallel.ForEach использует пул потоков. Насколько я понимаю, он будет создавать X количество потоков / задач и выполнять 4 (4 ядра) одновременно. Но это должно быть полностью финским заданием 1, прежде чем задание 5 может начаться.

Отсюда и мой третий вариант:

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);
    }
}

Теперь, насколько я понимаю, эта опция все равно будет блокировать основной поток вTask.WaitAll(tasks.ToArray()); это нормально, потому что я не хочу, чтобы он убегал с созданием задач быстрее, чем они могут быть выполнены.

Но задачи, которые будут выполняться параллельно, должны использоватьThreadPool, и все Х число задач должны начинать выполняться одновременно, а не блокироваться или в последовательном порядке. (пул потоков будет переключаться между ними, когда они станут активными илиawaiting)

Теперь мой вопрос.

Есть ли у варианта 3 какое-либо преимущество в производительности по сравнению с вариантом 2.

В частности, в сценарии на стороне сервера с более высокой производительностью. В конкретном программном обеспечении, над которым я сейчас работаю. Там будет несколько экземпляров моего простого варианта использования выше. Т.е. несколько потребителей.

Меня интересуют теоретические различия и плюсы и минусы двух решений, и, возможно, даже лучший 4-й вариант, если он есть.

Ответы на вопрос(2)

Ваш ответ на вопрос