首页 > 解决方案 > 在 MinGW 中处理 32 个窗口限制(Windows 批处理文件)

问题描述

我是一名计算生物学家,我正在尝试使用单个命令运行大量类似的代码,但我的实现遇到了障碍。

我正在使用 NEURON 模拟环境,它使用 MinGW 作为其 Windows 界面,这是我的研究表明我的问题出现的地方。

目前,我正在使用批处理文件来运行所有这些类似的代码片段,以遍历“集合”子文件夹:

@echo off
for /D %%a in ("%cd%\all_cells\cell_*.*") do cd "%%a\sim1\" & START neuron sim.hoc

当我有超过 32 个子文件夹时会出现问题;其他实例将不会运行,并且会出现“控制台设备分配失败:控制台太多”错误。

我的研究表明这是 Cygwin/MinGW 的一个已知问题。

但是,当我现在处理数百个实例(每个都指一个模拟单元,我想收集数百个实例的统计信息时)手动解决这个问题(确保不超过 32 个“集合”文件夹)非常耗时),所以我正在努力寻找解决方案。

也就是说,我在编写批处理文件方面很糟糕(我是一个习惯于科学语言的糟糕程序员),我不知道如何围绕这个编写代码。

如果有人可以帮助我找到绕过 32 个限制的方法,或者如果失败了,帮助我编写一个可以执行此操作的批处理文件,那就太好了:

- 迭代多达 32 个文件夹 - 等待实例完成 - 在接下来的 32 个文件夹中再做一次,直到我到达文件夹的末尾。

我曾尝试使用 /wait 命令一次执行一个,但它仍然会打开所有 32 个。(这并不理想,因为我想使用我拥有的所有 16 个内核。

标签: batch-filewindows-10cygwinmingw

解决方案


以下内容改编自https://stackoverflow.com/a/11715437/1012053,展示了如何运行任意数量的进程,同时限制并行同时运行的总数。请参阅该帖子以获得一些解释,尽管下面的代码已经很好地记录了注释。

我已经从原始脚本中删除了 /O 选项和使用 PSEXEC 的代码。

下面的脚本在一个窗口中运行所有内容 - 每个进程的输出都被捕获到一个临时锁定文件中,完成后,每个进程的完整输出都被输入到屏幕上,没有任何进程输出交错。还会显示每个过程的开始和结束时间。当然,如果您想将所有内容捕获到单个文件中,您可以重定向主脚本的输出。

我将并行进程的总数限制为 16 - 当然您可以轻松修改该限制。

如果您的任何文件夹路径包含该!字符,则该代码将无法正常工作。这可以通过一些额外的代码来解决。

除此之外,只要我没有犯任何愚蠢的错误,代码应该可以工作。我没有测试这个脚本,尽管它所派生的脚本已经过彻底的测试。

@echo off
setlocal enableDelayedExpansion

:: Define the maximum number of parallel processes to run.
set "maxProc=16"

:: 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 /D %%A in ("%cd%\all_cells\cell_*.*") do (
    if !startCount! lss %maxProc% (
      set /a "startCount+=1, nextProc=startCount"
    ) else (
      call :wait
    )
    set "cmd!nextProc!=%%A"
    echo -------------------------------------------------------------------------------
    echo !time! - proc!nextProc!: starting %%A
    2>nul del %lock%!nextProc!

    cd "%%A\sim1\"

    %= Redirect the output to the lock file and execute the command. The CMD process =%
    %= will maintain an exclusive lock on the lock file until the process ends.      =%
    start /b "" cmd /c 1^>"%lock%!nextProc!" 2^>^&1 neuron sim.hoc
  )
  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 =%
      echo ===============================================================================
      echo !time! - proc%%N: finished !cmd%%N!
      type "%lock%%%N"
      if defined launch (
        set nextProc=%%N
        exit /b
      )
      set /a "endCount+=1, endProc%%N=1"
    )
  )
  if %endCount% lss %startCount% (
    timeout 1 /nobreak >nul
    goto :wait
  )

2>nul del %lock%*
echo ===============================================================================
echo Thats all folks^^!

推荐阅读