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-й вариант, если он есть.