批处理如何才能仅降低运行它的 cmd 的优先级?

批处理如何才能仅降低运行它的 cmd 的优先级?

我有一个批处理,它运行一系列命令,这些命令会很长并且占用大量 CPU。我不介意它什么时候完成,所以我希望它只使用空闲的 CPU 周期。如果我cmd.exe在同一批处理中设置运行批处理的进程(一些)的优先级,则任何子进程都应继承其优先级。我知道你可以使用以下方法更改进程优先级wmic。 例如:

wmic process where name="cmd.exe" CALL setpriority "idle"

确实会完成工作,但是它也会降低cmd.exe当时正在运行的任何其他实例,这可能并不总是合适的。

有没有办法识别哪个进程正在运行批处理并仅设置其优先级idle

需要澄清的是:我正在寻找一种通用方法,您可以将其包含在任何此类批次或任何人都.bat可以调用以降低的批次中。

这是我有时会发现的一个问题,但是在我现在正在编写的具体内容中.bat,我传递了可变数量的文件,一次处理一个。

答案1

一种方法是首先找到 PID(“进程 ID”的缩写)。

WMIC 进程角色不仅支持设置优先级,还可以获取详细信息。例如以下内容(在一行上运行):

WMIC process where name="cmd.exe" get Caption,CommandLine,Name,ParentProcessId,ProcessId /FORMAT:LIST

(当然,这应该从传统的命令提示符运行,就像您问题中的示例一样。使用 PowerShell 需要额外的转义。)

但是,您可能有多个 CMD.EXE 副本,这可能会导致一些问题。如果您可以控制批处理文件的启动方式,这可能会更容易。 Dean 在 StackOverflow.com 上提出的问题“获取 Bat 文件中运行的 exe 的进程 ID”提供了多种将 PID 放入环境变量的解决方案。我建议您查看这些解决方案以确定哪种解决方案最适合您,但这里有一种可能的方法:

C:\> wmic Process WHERE "CommandLine LIKE '%%MyProgWMICflag%%'" Get ParentProcessID /format:list

这应该会向您显示 WMIC 命令的 ParentProcessID,它将是调用 WMIC 的程序(大概是您的 shell)的 PI​​D。

这是基于上述获取 PID 的解决方案的超链接页面中的 foxidrive 的回答。 (如果您愿意,该答案还显示了将其放入环境变量中。)

然后,将 ProcessId 包含在 WHERE 子句中。例如:

WMIC process where "name='cmd.exe' AND ProcessId='12345'" CALL setpriority "idle"

注意:这只是一种方法。您可能会发现其他一些常见特征更适合测试。您可以使用以下方法查看要检查的可用属性列表:
WMIC /OUTPUT:"Results.txt" Process Get /FORMAT:LIST

答案2

这是一个可以从另一个版本中使用的工作,用于设置批处理的优先级。如果您将其复制到目录中,则可以在需要时随时调用它,等等。像往常一样。感谢SetMyPrio.bat MyPrio.bat%PATH%托加姆,这给了我编写此代码的工具和方法。变量UniqueString确保精确定位的进程正是您需要的进程,而不是您的批处理中的任何其他实例(不太可能,但可能发生的情况)。

@echo off

GOTO :EndOfReminder

<HELP>

USAGE:

From any batch file or command prompt, write
   CALL MyPrio [/?|/h] [<priority>|get]
to set the priority of the caller cmd. Default for <priority> is idle.

/?, /h        This help.

<priority>    If you want your priority change to 
                 idle          -- no parameter, 64, idle or "idle"
                 below normal  -- 16384 or "below normal"
                 normal        -- 32, normal or "normal"
                 above normal  -- 32768 or "above normal"
                 high priority -- 128 or "high priority"
                 real time     -- 256, realtime or "realtime"
get           Returns current priority as errorlevel

TO DO
   - Parameter error control.
   - Support for Unicode characters in help.
</HELP>

:EndOfReminder
setlocal
set Priority=%~1

if /I ["%Priority%"]==["/h"] ( call :PrintHelp & exit /b )
if ["%Priority%"]==["/?"] ( call :PrintHelp & exit /b )

set UniqueString=%RANDOM%-%RANDOM%-%RANDOM%-%RANDOM%-%RANDOM%

if ["%Priority%"]==["get"] for /f "usebackq tokens=2 delims==" %%a in (`wmic Process WHERE "name='cmd.exe' AND CommandLine LIKE '%%%UniqueString%%%'" Get Priority /format:LIST`) do exit /b %%a

if not defined Priority set Priority=idle
for /f "usebackq tokens=2 delims==" %%a in (`wmic Process WHERE "name='cmd.exe' AND CommandLine LIKE '%%%UniqueString%%%'" Get ParentProcessID /format:LIST`) do set myPID=%%a
wmic process where "ProcessId='%myPID%'" CALL setpriority "%Priority%" > nul 2>&1

endlocal
goto :eof

:PrintHelp
setlocal DisableDelayedExpansion
call :eln 0
set "levelerror=^%%errorlevel^%%"
for /F "delims=" %%a in ('find /v /n "" "%~f0"') do (
   set "line=%%a"

   REM We store errorlevel in tmpel because setlocal will set errorlevel to 0...
   call set tmpel=%levelerror%
   setlocal EnableDelayedExpansion
   REM ... and now we restore it
   REM %levelerror% was expanded to %errorlevel% before running the iteration
   REM and the CALL SET allows actual errorlevel be stored in tmpel
   REM Finally, we set errorlevel again
   call :eln !tmpel!

   set "line=!line:*]=!"

   if /i "!line!"=="</HELP>" exit /b 0
   if errorlevel 1 echo(!line!
   if /i "!line!"=="<HELP>" call :eln 1
REM errorlevel is the only value that survives the following endlocal
   endlocal
)
exit /b 1

:eln
REM Sets errorlevel to %1
exit /b %1

编辑(2018-06-12)

  • 更名为我的Prio,添加选项/?/hget
  • 选项/?并打印您在和/h之间写入的提醒文本。此方法可以在您需要的任何其他批次中重复使用。<HELP></HELP>

如果你在批处理开始时调用MyPrio.bat,无论何时启动它,无论是从命令行还是从 Windows 资源管理器中拖放文件(成为参数),每个部分它(和子进程)将按照您期望的优先级运行。

例如:

@echo off
call MyPrio

REM Here comes a set of commands that will run with idle priority.

call MyPrio normal

REM Here may come some interactive part to request information needed to continue.

call MyPrio

REM Back to idle (or put any other priority you prefer).

call MyPrio normal

REM You may want to set the priority to normal at the end just in case you call the batch from a command line.

评论

PrioTest.bat我制作了一个名为test 的批处理,MyPrio.bat其中包含所有可能的选项,并对其进行排序,以便任何调用都会将优先级更改为其他优先级。我制作了一个子程序(:test1),它:

1.获取当前优先级并将其保存到myPrioBefore
2.MyPrio使用测试参数进行调用。
3.获取新优先级并将其保存至myPrioAfter
4.开始隐藏ping -t localhost
5.获取进程的优先级和 PIDping并将它们保存到 pingPriopingPID
6.打印 的值myPrioBefore、对 的调用MyPriomyPrioAfterpingPrio
7.杀死开始的ping(确保不是另一个)。

@echo off

setlocal

set UniqueString=%RANDOM%-%RANDOM%-%RANDOM%-%RANDOM%-%RANDOM%
for /f "usebackq tokens=2 delims==" %%a in (`wmic Process WHERE "name='cmd.exe' AND CommandLine LIKE '%%%UniqueString%%%'" Get ParentProcessID /format:LIST`) do set myPID=%%a
for %%a in (64 16384 32 32768 128 256 idle "below normal" none normal "idle" "normal" "above normal" realtime "high priority" "realtime") do call :test1 %%a
CALL MyPrio normal

endlocal
goto :eof

:test1
for /f "usebackq tokens=2 delims==" %%b in (`wmic Process WHERE "ProcessID='%%myPID%%'" Get Priority /format:LIST`) do set myPrioBefore=%%b
if [%1]==[none] ( call MyPrio ) else ( call MyPrio %1 )
for /f "usebackq tokens=2 delims==" %%b in (`wmic Process WHERE "ProcessID='%%myPID%%'" Get Priority /format:LIST`) do set myPrioAfter=%%b
start "" /b ping -t localhost > nul
for /f "usebackq tokens=2 delims==" %%b in (`wmic Process WHERE "name='ping.exe' AND ParentProcessID='%%myPID%%'" Get Priority /format:LIST`) do set pingPrio=%%b
for /f "usebackq tokens=2 delims==" %%b in (`wmic Process WHERE "name='ping.exe' AND ParentProcessID='%%myPID%%'" Get ProcessID /format:LIST`) do set pingPID=%%b
echo myPrioBefore==%myPrioBefore% / CALL MyPrio %1 / myPrioAfter==%myPrioAfter% / pingPrio==%pingPrio%
taskkill /f /pid %pingPID% > nul
exit /b

这是测试批次的输出:

myPrioBefore==4 / CALL MyPrio 64 / myPrioAfter==4 / pingPrio==4
myPrioBefore==4 / CALL MyPrio 16384 / myPrioAfter==6 / pingPrio==6
myPrioBefore==6 / CALL MyPrio 32 / myPrioAfter==8 / pingPrio==8
myPrioBefore==8 / CALL MyPrio 32768 / myPrioAfter==10 / pingPrio==8
myPrioBefore==10 / CALL MyPrio 128 / myPrioAfter==13 / pingPrio==8
myPrioBefore==13 / CALL MyPrio 256 / myPrioAfter==13 / pingPrio==8
myPrioBefore==13 / CALL MyPrio idle / myPrioAfter==4 / pingPrio==4
myPrioBefore==4 / CALL MyPrio "below normal" / myPrioAfter==6 / pingPrio==6
myPrioBefore==6 / CALL MyPrio none / myPrioAfter==4 / pingPrio==4
myPrioBefore==4 / CALL MyPrio normal / myPrioAfter==8 / pingPrio==8
myPrioBefore==8 / CALL MyPrio "idle" / myPrioAfter==4 / pingPrio==4
myPrioBefore==4 / CALL MyPrio "normal" / myPrioAfter==8 / pingPrio==8
myPrioBefore==8 / CALL MyPrio "above normal" / myPrioAfter==10 / pingPrio==8
myPrioBefore==10 / CALL MyPrio realtime / myPrioAfter==13 / pingPrio==8
myPrioBefore==13 / CALL MyPrio "high priority" / myPrioAfter==13 / pingPrio==8
myPrioBefore==13 / CALL MyPrio "realtime" / myPrioAfter==13 / pingPrio==8

正如您所看到的,myPrioAfter与调用中的请求不匹配,并且有时,的优先级ping不等于父级cmd,因此值必须更多(查看第五行:当前优先级为 10,您请求 128,新优先级为 13,子进程的优先级为 8,对不起?)。此外,realtime似乎没有效果(我认为需要管理员权限)。

答案3

这是我的看法:不要降低当前进程的优先级,而是通过生成cmd.exe具有较低优先级的新实例来恢复批处理文件;环境将被(自动)继承,并且如果在开始时完成,它将表现得几乎“相同”,除了进行Ctrl+c处理(这start /b会禁止使用 -代替)。这不会引入任何额外的依赖关系,但会以额外实例(以及相关的)Ctrl+Break的(低)成本引入。cmd.execonhost.exe

只需复制并粘贴此代码片段:

rem | reduce process priority (uses an additional cmd.exe instance)
if not "%TARGET_LABEL%"=="" goto %TARGET_LABEL%
set TARGET_LABEL=RunLowPriority
start "" /b /belownormal /wait cmd /d /c "%~dpnx0" %*
goto :EOF
:RunLowPriority

怎么了:

  • [第二个实例] 检测到先前的设置:跳转到所需位置 ( RunLowPriority)
  • RunLowPriority[第一个实例]为第二个实例设置跳转目标 ( )
  • [第一个实例] 启动第二个实例
    • 使用低于一般优先事项
    • 不要启动新的控制台并等待第二个实例结束
    • 使用引号中的完整路径以避免“意外”(空格等)
    • 传递所有参数(%*
  • [第一个实例] 当第二个实例结束时退出

使用两个命令提示符实例的附带影响是Ctrl+Break显示 终止批处理作业 (Y/N)?- 以交互方式运行脚本时没有问题(例如,Windows 资源管理器),但从现有命令提示符运行时有点奇怪。

答案4

您需要使用 START 运行批处理(从另一个批处理) - 然后开始可以设置为单独的优先级

START "" batch.bat /B /LOW

相关内容