【问题标题】:Parallel execution of shell processesshell进程的并行执行
【发布时间】:2010-10-14 22:27:55
【问题描述】:

是否有可用于在 Windows 批处理文件中并行执行多个进程的工具?我找到了一些有趣的 Linux 工具(parallelPPSS),但是,我需要一个用于 Windows 平台的工具。

奖励:如果该工具还允许以简单的方式在多台机器之间分配进程,远程运行进程,那将是很棒的 PsExec

示例:我希望在下面的 for 循环中使用它

for %F in (*.*) do processFile.exe %F

并行运行的 processFile.exe 实例数量有限,以利用多核 CPU。

【问题讨论】:

    标签: windows batch-file shell parallel-processing multicore


    【解决方案1】:

    编辑 - 我修改了脚本以选择性地显示每个进程的输出

    这是一个本地批处理解决方案,可以可靠地并行运行一系列命令,一次不会启动超过 n 个进程。

    它甚至内置了一种机制,可以通过 PSEXEC 将进程分配到特定的 CPU 或远程机器,但我还没有测试过这个功能。

    完成这项工作的技巧是通过 CMD 进程启动每个命令,该进程将 stdout 或未定义的句柄重定向到锁定文件。该进程将在文件上保持一个排他锁,直到它终止。进程如何终止(正常退出、崩溃、终止进程)无关紧要,一旦终止,锁就会被释放。

    主脚本可以通过尝试重定向到同一个锁定文件来测试进程是否仍然处于活动状态。如果进程仍然处于活动状态,则重定向将失败,如果已终止,则重定向成功。

    默认情况下,脚本会忽略每个进程的输出。如果以/O 选项作为第一个参数开始,则它会显示每个进程的输出,而不会交错。

    我的演示将进程限制设置为 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="
    )
    
    :: The list of commands could come from anywhere such as another file
    :: or the output of another command. For this demo I will list the
    :: commands within this script - 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
    :: Modify the IN () clause as needed to retrieve the list of commands
      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!
    

    【讨论】:

    • EDIT - 在 :wait 内移动重定向以更接近相关的 cmets
    【解决方案2】:

    试试start:

    start "title of the process" "P:\ath\to.exe"
    

    它会打开一个具有给定标题的新窗口并执行 BAT、CMD 或 EXE 文件。还可以设置优先级,设置相同的环境等。

    无法执行的文件用相关程序打开。

    延伸阅读:开始->运行

    cmd /k start /?
    

    至少在 WinME 之后开始可用。

    祝你好运!

    【讨论】:

    • 感谢您的回答。但是,我发现一个小问题是无法控制创建多少个 start 实例。理想情况下,这将是一个小于或等于可用 CPU 数量的数字。
    • 不客气。试试约翰内斯的回答。似乎还有更多内容:)
    【解决方案3】:

    听起来更像您想使用 Powershell 2。但是,您可以使用 start 生成新的 cmd 窗口(或其他进程),另请参阅 this 答案。尽管您可能必须使用一些其他工具和一些小技巧来创建类似“进程池”的东西(一次最多只能运行 n 个实例)。您可以通过使用tasklist /im 并计算已经存在的数量(for 循环或wc,如果适用)来实现后者,然后只需等待(ping -n 2 ::1 &gt;nul 2&gt;&amp;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 远程生成进程(但不太可行,因为您必须在另一台机器上拥有管理员权限并在批处理中提供密码)。但是,您必须使用进程名称进行过滤。

    【讨论】:

      【解决方案4】:

      有一个基本的 Windows xargs-like-clone 支持 http://www.pirosa.co.uk/demo/wxargs/wxargs.html 处的 -P 并行处理选项

      【讨论】:

      • 这是最方便的解决方案。
      【解决方案5】:

      Linux 下的 GNU xargs 有一个“-P n”开关来并行启动“n”个进程。

      也许 xargs 的 cygwin/mingw 构建也支持这个?

      那么你可以使用:

      xargs -P 4 processFile < fileList
      

      不过,没有花哨的多节点进程生成。

      【讨论】:

      • 当 Windows 有内置机制时,要求 MinGW 是一个非常笨拙的解决方案。
      • @samoz:什么是 Windows 批处理机制?
      • @MarkKCowan stackoverflow.com/questions/11010834/… 另请参阅下面@guerda 的答案。不过,start 不是那么灵活。
      【解决方案6】:

      我编写了一个提供多线程支持的库(以“模拟”线程池行为的方式)

      MultiBat on github

      这是该库的内联版本,但我最好转到上面的链接以获取最新版本(以及不需要内联到您的 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 -----------------------------------
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2019-04-19
        • 2023-03-04
        • 1970-01-01
        • 2014-06-29
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多