Многопоточная Windows FOR пакетная команда

11

Знаете ли вы, если есть простой способ запустить команду FOR в пакетном файле в нескольких потоках? Какой смысл иметь 4 ядра, если я не могу выполнять свои задачи в 4 параллельных потоках?

Например, если я оптимизирую PNG с помощью PNGOUT, я бы использовал команду

for %i in (*.png) do pngout "%i"

Но это очень параллелизуемая задача, в которой подзадачи вообще не зависят друг от друга.

Чтобы запустить это в 4 «очереди», я бы написал что-то вроде

for -thread 4 %i in (*.png) do pngout "%i"

Нужно ли мне писать свое собственное приложение, которое могло бы сделать это, или есть бесплатное решение?

Axarydax
источник
проверьте следующий ответ на использование внешнего инструмента: [ stackoverflow.com/a/12041213/365229][1] [1]: stackoverflow.com/a/12041213/365229
Behrouz.M

Ответы:

13

Я написал пакетный файл, который некоторое время назад выполняет только максимальное количество команд в Stack Overflow: Параллельное выполнение процессов оболочки :

@echo off
for /l %%i in (1,1,20) do call :loop %%i
goto :eof

:loop
call :checkinstances
if %INSTANCES% LSS 5 (
    rem just a dummy program that waits instead of doing useful stuff
    rem but suffices for now
    echo Starting processing instance for %1
    start /min wait.exe 5 sec
    goto :eof
)
rem wait a second, can be adjusted with -w (-n 2 because the first ping returns immediately;
rem otherwise just use an address that's unused and -n 1)
echo Waiting for instances to close ...
ping -n 2 ::1 >nul 2>&1
rem jump back to see whether we can spawn a new process now
goto loop
goto :eof

:checkinstances
rem this could probably be done better. But INSTANCES should contain the number of running instances afterwards.
for /f "usebackq" %%t in (`tasklist /fo csv /fi "imagename eq wait.exe"^|find /v /c ""`) do set INSTANCES=%%t
goto :eof

Он порождает максимум четыре новых процесса, которые выполняются параллельно и свернуты. Время ожидания должно быть скорректировано, вероятно, в зависимости от того, сколько работает каждый процесс и как долго он выполняется. Вам, вероятно, также необходимо настроить имя процесса, для которого ищет список задач, если вы делаете что-то еще.

Однако нет способа правильно подсчитать процессы, порожденные этим пакетом. Одним из способов может быть создание случайного числа в начале пакета ( %RANDOM%) и создание вспомогательного пакета, который выполняет обработку (или порождает программу обработки), но который может установить свой заголовок окна для параметра:

@echo off
title %1
"%2" "%3"

Это будет простой пакет, который устанавливает свой заголовок на первый параметр, а затем запускает второй параметр с третьим в качестве аргумента. Затем вы можете отфильтровать список задач, выбрав только процессы с указанным заголовком окна ( tasklist /fi "windowtitle eq ..."). Это должно работать достаточно надежно и предотвращать слишком много ложных срабатываний. Поиск cmd.exeбыл бы плохой идеей, если у вас все еще есть запущенные экземпляры, так как это ограничивает ваш пул рабочих процессов.

Вы можете использовать %NUMBER_OF_PROCESSORS%для создания разумного значения по умолчанию, сколько экземпляров будет создано.

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

детеныш
источник
это какая-то серьезная черная магия! Хотел бы я поднять голос еще раз!
Axarydax
Нет, это довольно простые вещи, как только ты освоишь их ;-)
Джои
Команда 'wc' недоступна в моей командной строке Win10. Что я могу использовать еще?
PeterCo
1
@PeterCo: find /c /v "".
Джои
1

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

Кроме того, Windows PowerShell может предоставить больше возможностей, чтобы приблизить вас к цели.

Если вы просто хотите запустить несколько операций одновременно, вы можете использовать команду start для запуска утилиты PNGOUT в новом окне. Цикл FOR будет продолжен без ожидания завершения каждой операции.

Модифицированная строка будет выглядеть так:

for %i in (*.png) do start pngout "%i"

Однако обратите внимание, что это эффективно запустит PNGOUT для ВСЕХ файлов в каталоге одновременно, что, скорее всего, нежелательно.

Herohtar
источник
Это наивный подход и, как вы упомянули, вероятно, нежелателен. Вам нужно только запустить столько процессов, сколько у вас есть ядер. Если у вас есть 4 ядра и 1000 файлов, вы действительно хотите обрабатывать только 4 за один раз, пока все 1000 не будут обработаны. Если вы попытаетесь обработать все 1000 одновременно на 4 ядрах, ваш компьютер замедлится до бесполезного сканирования.
Adisak