Параллельное выполнение процессов оболочки
Существует ли инструмент для параллельного выполнения нескольких процессов в командном файле Windows? Я нашел некоторые интересные инструменты для Linux ( параллельные и PPSS), однако мне понадобится инструмент для платформ Windows.
Бонус: Было бы замечательно, если бы инструмент позволял также легко распределять процессы между несколькими машинами, запуская процессы удаленно, как PsExec.
Пример: я хотел бы, чтобы в следующем для цикла
for %F in (*.*) do processFile.exe %F
ограниченное количество экземпляров processFile.exe работают параллельно, чтобы использовать преимущества многоядерных процессоров.
5 ответов
GNU xargs в Linux имеет переключатель "-P n" для параллельного запуска "n" процессов.
Может быть, сборка cygwin/mingw xargs также поддерживает это?
Тогда вы можете использовать:
xargs -P 4 processFile < fileList
Впрочем, не нужно создавать многоузловые процессы.
Изменить - я изменил скрипт, чтобы при желании отображать вывод каждого процесса
Вот нативное пакетное решение, которое надежно выполняет список команд параллельно, никогда не запуская более чем n процессов одновременно.
В него даже встроен механизм для распределения процессов по конкретным процессорам или удаленным компьютерам через PSEXEC, но я не тестировал эту функцию.
Хитрость, чтобы сделать эту работу, состоит в том, чтобы запустить каждую команду через процесс CMD, который перенаправляет либо стандартный вывод, либо неопределенный дескриптор в файл блокировки. Процесс будет поддерживать эксклюзивную блокировку файла, пока не завершится. Не имеет значения, как завершается процесс (обычный выход, сбой, остановленный процесс), блокировка будет снята, как только она это сделает.
Главный сценарий может проверить, если процесс все еще активен, пытаясь перенаправить на тот же файл блокировки. Перенаправление не будет выполнено, если процесс все еще активен, и завершится успешно, если он завершился.
По умолчанию скрипт игнорирует выходные данные каждого процесса. Если началось с /O
параметр в качестве 1-го параметра, то он отображает вывод каждого процесса, без чередования.
Моя демонстрация устанавливает предел процесса равным 4, и просто запускает серию команд PING различной длины.
Я проверял это на XP, Vista и Windows 7.
@echo off
setlocal enableDelayedExpansion
:: Display the output of each process if the /O option is used
:: else ignore the output of each process
if /i "%~1" equ "/O" (
set "lockHandle=1"
set "showOutput=1"
) else (
set "lockHandle=1^>nul 9"
set "showOutput="
)
:: List of commands goes here. Each command is prefixed with :::
::: ping /n 05 ::1
::: ping /n 20 ::1
::: ping /n 10 ::1
::: ping /n 15 ::1
::: ping /n 07 ::1
::: ping /n 05 ::1
::: ping /n 20 ::1
::: ping /n 10 ::1
::: ping /n 15 ::1
::: ping /n 07 ::1
:: Define the maximum number of parallel processes to run.
:: Each process number can optionally be assigned to a particular server
:: and/or cpu via psexec specs (untested).
set "maxProc=4"
:: Optional - Define CPU targets in terms of PSEXEC specs
:: (everything but the command)
::
:: If a cpu is not defined for a proc, then it will be run on the local machine.
:: I haven't tested this feature, but it seems like it should work.
::
:: set cpu1=psexec \\server1 ...
:: set cpu2=psexec \\server1 ...
:: set cpu3=psexec \\server2 ...
:: etc.
:: For this demo force all cpu specs to undefined (local machine)
for /l %%N in (1 1 %maxProc%) do set "cpu%%N="
:: Get a unique base lock name for this particular instantiation.
:: Incorporate a timestamp from WMIC if possible, but don't fail if
:: WMIC not available. Also incorporate a random number.
set "lock="
for /f "skip=1 delims=-+ " %%T in ('2^>nul wmic os get localdatetime') do (
set "lock=%%T"
goto :break
)
:break
set "lock=%temp%\lock%lock%_%random%_"
:: Initialize the counters
set /a "startCount=0, endCount=0"
:: Clear any existing end flags
for /l %%N in (1 1 %maxProc%) do set "endProc%%N="
:: Launch the commands in a loop
set launch=1
for /f "tokens=* delims=:" %%A in ('findstr /b ":::" "%~f0"') do (
if !startCount! lss %maxProc% (
set /a "startCount+=1, nextProc=startCount"
) else (
call :wait
)
set cmd!nextProc!=%%A
if defined showOutput echo -------------------------------------------------------------------------------
echo !time! - proc!nextProc!: starting %%A
2>nul del %lock%!nextProc!
%= Redirect the lock handle to the lock file. The CMD process will =%
%= maintain an exclusive lock on the lock file until the process ends. =%
start /b "" cmd /c %lockHandle%^>"%lock%!nextProc!" 2^>^&1 !cpu%%N! %%A
)
set "launch="
:wait
:: Wait for procs to finish in a loop
:: If still launching then return as soon as a proc ends
:: else wait for all procs to finish
:: redirect stderr to null to suppress any error message if redirection
:: within the loop fails.
for /l %%N in (1 1 %startCount%) do 2>nul (
%= Redirect an unused file handle to the lock file. If the process is =%
%= still running then redirection will fail and the IF body will not run =%
if not defined endProc%%N if exist "%lock%%%N" 9>>"%lock%%%N" (
%= Made it inside the IF body so the process must have finished =%
if defined showOutput echo ===============================================================================
echo !time! - proc%%N: finished !cmd%%N!
if defined showOutput type "%lock%%%N"
if defined launch (
set nextProc=%%N
exit /b
)
set /a "endCount+=1, endProc%%N=1"
)
)
if %endCount% lss %startCount% (
1>nul 2>nul ping /n 2 ::1
goto :wait
)
2>nul del %lock%*
if defined showOutput echo ===============================================================================
echo Thats all folks!
Вот вывод из примера запуска, который игнорирует вывод процесса
12:24:07.52 - proc1: starting ping /n 05 ::1
12:24:07.52 - proc2: starting ping /n 20 ::1
12:24:07.53 - proc3: starting ping /n 10 ::1
12:24:07.54 - proc4: starting ping /n 15 ::1
12:24:11.60 - proc1: finished ping /n 05 ::1
12:24:11.60 - proc1: starting ping /n 07 ::1
12:24:16.66 - proc3: finished ping /n 10 ::1
12:24:16.66 - proc3: starting ping /n 05 ::1
12:24:17.68 - proc1: finished ping /n 07 ::1
12:24:17.68 - proc1: starting ping /n 20 ::1
12:24:20.72 - proc3: finished ping /n 05 ::1
12:24:20.72 - proc3: starting ping /n 10 ::1
12:24:21.75 - proc4: finished ping /n 15 ::1
12:24:21.75 - proc4: starting ping /n 15 ::1
12:24:26.82 - proc2: finished ping /n 20 ::1
12:24:26.82 - proc2: starting ping /n 07 ::1
12:24:29.86 - proc3: finished ping /n 10 ::1
12:24:32.89 - proc2: finished ping /n 07 ::1
12:24:35.92 - proc4: finished ping /n 15 ::1
12:24:36.93 - proc1: finished ping /n 20 ::1
Thats all folks
Вот вывод, если запустить с /O
опция, отображающая вывод процесса
-------------------------------------------------------------------------------
12:24:51.02 - proc1: starting ping /n 05 ::1
-------------------------------------------------------------------------------
12:24:51.02 - proc2: starting ping /n 20 ::1
-------------------------------------------------------------------------------
12:24:51.03 - proc3: starting ping /n 10 ::1
-------------------------------------------------------------------------------
12:24:51.04 - proc4: starting ping /n 15 ::1
===============================================================================
12:24:55.10 - proc1: finished ping /n 05 ::1
Pinging ::1 with 32 bytes of data:
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Ping statistics for ::1:
Packets: Sent = 5, Received = 5, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
Minimum = 0ms, Maximum = 0ms, Average = 0ms
-------------------------------------------------------------------------------
12:24:55.10 - proc1: starting ping /n 07 ::1
===============================================================================
12:25:00.17 - proc3: finished ping /n 10 ::1
Pinging ::1 with 32 bytes of data:
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Ping statistics for ::1:
Packets: Sent = 10, Received = 10, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
Minimum = 0ms, Maximum = 0ms, Average = 0ms
-------------------------------------------------------------------------------
12:25:00.19 - proc3: starting ping /n 05 ::1
===============================================================================
12:25:01.22 - proc1: finished ping /n 07 ::1
Pinging ::1 with 32 bytes of data:
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Ping statistics for ::1:
Packets: Sent = 7, Received = 7, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
Minimum = 0ms, Maximum = 0ms, Average = 0ms
-------------------------------------------------------------------------------
12:25:01.23 - proc1: starting ping /n 20 ::1
===============================================================================
12:25:04.27 - proc3: finished ping /n 05 ::1
Pinging ::1 with 32 bytes of data:
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Ping statistics for ::1:
Packets: Sent = 5, Received = 5, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
Minimum = 0ms, Maximum = 0ms, Average = 0ms
-------------------------------------------------------------------------------
12:25:04.28 - proc3: starting ping /n 10 ::1
===============================================================================
12:25:05.30 - proc4: finished ping /n 15 ::1
Pinging ::1 with 32 bytes of data:
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Ping statistics for ::1:
Packets: Sent = 15, Received = 15, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
Minimum = 0ms, Maximum = 0ms, Average = 0ms
-------------------------------------------------------------------------------
12:25:05.32 - proc4: starting ping /n 15 ::1
===============================================================================
12:25:10.38 - proc2: finished ping /n 20 ::1
Pinging ::1 with 32 bytes of data:
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Ping statistics for ::1:
Packets: Sent = 20, Received = 20, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
Minimum = 0ms, Maximum = 0ms, Average = 0ms
-------------------------------------------------------------------------------
12:25:10.40 - proc2: starting ping /n 07 ::1
===============================================================================
12:25:13.44 - proc3: finished ping /n 10 ::1
Pinging ::1 with 32 bytes of data:
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Ping statistics for ::1:
Packets: Sent = 10, Received = 10, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
Minimum = 0ms, Maximum = 0ms, Average = 0ms
===============================================================================
12:25:16.48 - proc2: finished ping /n 07 ::1
Pinging ::1 with 32 bytes of data:
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Ping statistics for ::1:
Packets: Sent = 7, Received = 7, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
Minimum = 0ms, Maximum = 0ms, Average = 0ms
===============================================================================
12:25:19.52 - proc4: finished ping /n 15 ::1
Pinging ::1 with 32 bytes of data:
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Ping statistics for ::1:
Packets: Sent = 15, Received = 15, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
Minimum = 0ms, Maximum = 0ms, Average = 0ms
===============================================================================
12:25:20.54 - proc1: finished ping /n 20 ::1
Pinging ::1 with 32 bytes of data:
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Ping statistics for ::1:
Packets: Sent = 20, Received = 20, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
Minimum = 0ms, Maximum = 0ms, Average = 0ms
===============================================================================
Thats all folks
Пытаться start
:
start "title of the process" "P:\ath\to.exe"
Он открывает новое окно с заданным заголовком и выполняет файл BAT, CMD или EXE. Вы также можете установить приоритет, установить ту же среду и т. Д.
Файлы, которые не могут быть выполнены, открываются соответствующей программой.
Дальнейшее чтение: Пуск -> Выполнить
cmd /k start /?
Старт доступен как минимум с WinME.
Удачи!
Похоже, вы хотите использовать Powershell 2. Однако вы можете создавать новые cmd
окна (или другие процессы) с помощью start
Смотрите также этот ответ. Хотя вам, вероятно, придется использовать некоторые другие инструменты и небольшую хитрость, чтобы создать что-то вроде "пула процессов" (чтобы одновременно было запущено не более n экземпляров). Вы можете достичь последнего с помощью tasklist /im
и подсчитываешь сколько уже там (for
петля или wc
, если применимо) и просто ждать (ping -n 2 ::1 >nul 2>&1
) и еще раз проверьте, можете ли вы порождать новый процесс.
Я собрал небольшую тестовую партию для этого:
@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 /c /v ""`) do set INSTANCES=%%t
goto :eof
Он порождает максимум четыре новых процесса, которые выполняются параллельно и свернуты. Время ожидания должно быть скорректировано, вероятно, в зависимости от того, сколько работает каждый процесс и как долго он выполняется. Вам, вероятно, также нужно настроить имя процесса, для которого ищет список задач, если вы делаете что-то еще.
Однако нет способа правильно подсчитать процессы, порожденные этим пакетом. Одним из способов будет создание случайного числа в начале пакета (%RANDOM%
) и создайте вспомогательный пакет, который выполняет обработку (или порождает программу обработки), но может установить в своем заголовке окна параметр:
@echo off
title %1
"%2" "%3"
Это будет простой пакет, который устанавливает свой заголовок на первый параметр, а затем запускает второй параметр с третьим в качестве аргумента. Затем вы можете отфильтровать список задач, выбрав только процессы с указанным заголовком окна (tasklist /fi "windowtitle eq ..."
). Это должно работать достаточно надежно и предотвращать слишком много ложных срабатываний. В поисках cmd.exe
было бы плохой идеей, если у вас все еще есть запущенные экземпляры, так как это ограничивает ваш пул рабочих процессов.
Ты можешь использовать %NUMBER_OF_PROCESSORS%
чтобы создать разумное значение по умолчанию, сколько экземпляров будет создано.
Вы также можете легко адаптировать это для использования psexec
запускать процессы удаленно (но это не очень жизнеспособно, так как вам нужно иметь права администратора на другом компьютере, а также указать пароль в пакете). Тогда вам придется использовать имена процессов для фильтрации.
Существует простой xargs-like-clone для Windows, который поддерживает параметр параллельной обработки -P по адресу http://www.pirosa.co.uk/demo/wxargs/wxargs.html
Я написал библиотеку, которая обеспечивает поддержку многопоточности (таким образом, чтобы "имитировать" поведение пулов потоков)
Вот встроенная версия этой библиотеки, но мне достаточно перейти по ссылке выше для последней версии (и версии, которая не требует встраивания в ваш файл bat.
REM ---------------------------------------------------------------------------
REM ---------------------------------------------------------------------------
REM ---------------------------------------------------------------------------
goto:EOF
REM Append this to the END of your batch-file [*.BAT] to get inline "Multi" support
REM "Multi" is a thread-pool emulation helper library for controlling multi-threaded windows batch [*.BAT] files
REM Copyright (c) 2020 Adisak Pochanayon
REM Contact: adisak@gmail.com
REM See Multi_License.txt for details
REM -----------------------------------
:Multi_Setup
call :Multi_SetName %1
if "%2"=="" (
if "%NUMBER_OF_PROCESSORS%"=="" call :Multi_SetLimitToMax
) else (
call :Multi_SetLimit %2
)
goto:EOF
REM -----------------------------------
:Multi_SetName
REM Returns: MULTI_CHILDPROC_WINNAME - name to use for child processes (the window title)
if "%1"=="" (
SET MULTI_CHILDPROC_WINNAME=Multi-CmdProc
) else (
SET MULTI_CHILDPROC_WINNAME=Multi-CmdProc-%1
)
goto:EOF
REM -----------------------------------
REM To Enable Hyperthreading, call Multi_SetHyperThread before calling Multi_Setup or Multi_SetLimitToMax
:Multi_SetHyperThread
REM Parameter 1: (optional)
REM value=1 (or unspecified) - Use Hyperthreading if available
REM value=0 (or other) - Do not use Hyperthreading to compute the max threads
REM Returns: NumberOfCores - number of real CPU cores
REM Returns: MULTI_HAS_HYPERTHREADING - 1 if the CPU has Hyperthreading
REM Returns: MULTI_USE_HYPERTHREADING - 1 if "Multi" should use Hyperthreading
REM Set variable NumberOfCores
if "%NumberOfCores%"=="" (
for /f "tokens=*" %%f in ('wmic cpu get NumberOfCores /value ^| find "="') do set %%f
)
REM Set variable MULTI_HAS_HYPERTHREADING
if "%MULTI_HAS_HYPERTHREADING%"=="" (
if "%NumberOfCores%"=="%NUMBER_OF_PROCESSORS%" (
REM Non-Hyperthreading
SET MULTI_HAS_HYPERTHREADING=0
) else (
REM Hyperthreading
SET MULTI_HAS_HYPERTHREADING=1
)
}
if "%1"=="" (
SET MULTI_USE_HYPERTHREADING=%MULTI_HAS_HYPERTHREADING%
) else (
SET MULTI_USE_HYPERTHREADING=%1
)
REM Set the max threads to the limit (respecting Hyperthreading options)
call :Multi_SetLimitToMax
goto:EOF
REM -----------------------------------
:Multi_SetLimit
REM Parameter 1:
REM value=N - Use N as the number of max threads
REM unspecified - Compute the default number of max threads
REM Returns: MULTI_MAXCHILDREN - the maximum number of child processes to run simultaneously
if "%1"=="" (
if "%MULTI_MAXCHILDREN%"=="" call :Multi_SetLimitToMax
goto:EOF
)
SET /A MULTI_MAXCHILDREN=%1
if %MULTI_MAXCHILDREN% LSS 1 SET MULTI_MAXCHILDREN=1
goto:EOF
REM -----------------------------------
:Multi_SetLimitToMax
REM Parameter 1: (optional)
REM Passed to Multi_SetHyperThread if present
REM Returns: MULTI_MAXCHILDREN - max number of "threads" in pool for "Multi"
if "%1"=="" (
REM Check if Hyperthreading support was initialized
if "%NumberOfCores%"=="" (
call :Multi_SetHyperThread 0
REM Multi_SetHyperThread calls back to this subroutine so exit to prevent recursion
goto:EOF
)
) else (
call :Multi_SetHyperThread %1
REM Multi_SetHyperThread calls back to this subroutine so exit to prevent recursion
goto:EOF
)
if %NUMBER_OF_PROCESSORS% LEQ 3 (
SET MULTI_MAXCHILDREN=1
) else (
if "%NumberOfCores%"=="%NUMBER_OF_PROCESSORS%" (
REM Non-Hyperthreading
SET /A MULTI_MAXCHILDREN=%NUMBER_OF_PROCESSORS%-2
) else if "%MULTI_USE_HYPERTHREADING%"=="1" (
REM Hyperthreading available and used
SET /A MULTI_MAXCHILDREN=%NUMBER_OF_PROCESSORS%/2 - 1
) else (
REM Hyperthreading available but not used
SET /A MULTI_MAXCHILDREN=%NUMBER_OF_PROCESSORS%-2
)
)
goto:EOF
REM -----------------------------------
:Multi_RunWin
if "%MULTI_CHILDPROC_WINNAME%"=="" call :Multi_SetName
call :Multi_WaitChildrenMax
start "%MULTI_CHILDPROC_WINNAME%" /BELOWNORMAL cmd /c %*
goto:EOF
REM -----------------------------------
:Multi_RunWinMin
if "%MULTI_CHILDPROC_WINNAME%"=="" call :Multi_SetName
call :Multi_WaitChildrenMax
start "%MULTI_CHILDPROC_WINNAME%" /MIN /BELOWNORMAL cmd /c %*
goto:EOF
REM -----------------------------------
:Multi_RunSyncMin
REM Use this command to run things that mess with the window title
REM and otherwise would screw up the "Multi" System
start "Multi-Sync" /MIN /WAIT cmd /c %*
goto:EOF
REM -----------------------------------
:Multi_WaitChildrenMax
REM Wait until less than MULTI_MAXCHILDREN children are running
if "%MULTI_MAXCHILDREN%"=="" call :Multi_SetLimitToMax
call :Multi_WaitChildren %MULTI_MAXCHILDREN%
goto:EOF
REM -----------------------------------
:Multi_WaitChildren
SETLOCAL
REM multi_WAITCOUNT is a local variable
SET multi_WAITCOUNT=1
if "%1"=="" GOTO :loop_WaitChildren
SET /A multi_WAITCOUNT=%1
if %multi_WAITCOUNT% LSS 1 set multi_WAITCOUNT=1
:loop_WaitChildren
call :Multi_GetNumChildren
if %MULTI_NUM_CHILDREN% LSS %multi_WAITCOUNT% GOTO :exit_WaitChildren
timeout /t 1 /nobreak > nul
GOTO :loop_WaitChildren
:exit_WaitChildren
ENDLOCAL
goto:EOF
REM -----------------------------------
:Multi_GetNumChildren
REM Returns: MULTI_NUM_CHILDREN - the number of "children" processes (Windows named MULTI_CHILDPROC_WINNAME)
if "%MULTI_CHILDPROC_WINNAME%"=="" call :Multi_SetName
REM MULTI_NUM_CHILDREN should contain the number of
REM running %MULTI_CHILDPROC_WINNAME% instances after this
for /f "usebackq" %%t in (`tasklist /fo csv /fi "WINDOWTITLE eq %MULTI_CHILDPROC_WINNAME%" ^| find /c "cmd"`) do (
SET MULTI_NUM_CHILDREN=%%t
)
goto:EOF
REM -----------------------------------