Лучший подход для регулирования внешнего процесса в c # - PullRequest
0 голосов
/ 19 ноября 2018

Мне нужно написать приложение с графическим интерфейсом для обработки некоторых пакетов файлов на внешних инструментах командной строки. И мне нужно распараллеливать их по файлу и регулировать их по потокам ЦП, чтобы максимизировать использование ЦП и пропускную способность. Я сделал некоторые работы и некоторые исследования по этому вопросу:

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 #.

...