Мне нужно написать приложение с графическим интерфейсом для обработки некоторых пакетов файлов на внешних инструментах командной строки. И мне нужно распараллеливать их по файлу и регулировать их по потокам ЦП, чтобы максимизировать использование ЦП и пропускную способность. Я сделал некоторые работы и некоторые исследования по этому вопросу:
Parallel.ForEach
Когда я впервые задал этот вопрос в StackOverflow, кто-то посоветовал мне использовать Parallel.Foreach . Это работает; но он просто блокирует некоторые потоки и тратит процессор на ожидание внешних процессов. И если внешний процесс будет выполняться долго, это уменьшит потоки параллелизма! В конце концов я отказался от этого и попытался найти другие решения.
Semaphoreslim
Я просто использую
SemaphoreSlim sem = new SemaphoreSlim(Environment.ProcessorCount);
для регулирования количества внешних процессов
и просто используйте
await task.whenall(tasks);
для ожидания всего процесса без блокировки моей программы с графическим интерфейсом.
Теперь я использую это. Это работает очень хорошо.
Но есть только одна проблема: в MSDN упоминается, что semaphoreslim предназначен для одного процесса, когда ожидаемое время ожидания будет очень коротким .
Но в моем внешнем процессе он часто выполняется очень долго (время процесса зависит от типа и размера входного файла). Так что Spinwait тратит ресурсы процессора в моем случае. Поэтому мне действительно интересно, есть ли какие-то решения, чтобы избежать этого спин-вейта, но до сих пор я не могу найти его. Некоторые могут сказать, что используют традиционный семафор . Я пытался. Но семафор не может быть Awaitable, поэтому он заблокировал мой графический интерфейс, и если я использую
await Task.run()
с ним, тогда он не работает лучше, чем семафорлим .
TPL Dataflow
Другое решение, которое я нашел, - использовать библиотеку потоков данных TPL. Это немного лучше, чем semaphoreslim . Но некоторые из моих конкретных вариантов использования не могут быть реализованы в потоке данных TPL.
Например, у меня есть куча архивов. Мне нужно распаковать их и обработать файлы внутри каждого архива, а затем повторно сжать. В TPL Dataflow я думал разделить на «блок распаковки» (паларизм: 1), «блок обработки файла» (паларизм: 12) и «блок сжатия» (паларизм: 1). Но я не знаю, как ждать некоторых задач из всей задачи в потоке данных TPL. Если я не ошибаюсь, поток данных TPL может просто подождать, пока блок не будет завершен или нет. В моем случае, если файлы архива один обрабатываются, блок сжатия не имеет возможности это узнать. Необходимо подождать, пока все файлы будут обработаны.
Но в семафорлисе я могу использовать
await Task.whenall(someoftasks);
в каждой итерации каждого цикла архивов, чтобы ожидать их. Таким образом, я могу получить более высокую пропускную способность, поэтому я, наконец, разочаровался в использовании потока данных TPL.
Заключение
Так что после моего исследования я все еще использую семафорлим. Он работает очень хорошо, но я запутался в том, что он затрачивает ресурсы процессора. Поэтому мне интересно, есть ли лучший способ регулирования внешнего процесса в c #.