我正在制作一个使用 PowerShell 创建快捷方式的 Python 脚本。我利用了这样一个事实:如果网络路径不可用,则创建快捷方式将需要很长时间。如果进程花费太长时间,我会强制终止该进程,这会导致一堆可用计算机的快捷方式。这是循环内的内容
temp = "powershell; $WshShell = New-Object -ComObject WScript.Shell; $Shortcut = $WshShell.CreateShortcut(\"\"\"%cd%\\" + list[i][:list[i].find("\\")] + ".lnk\"\"\"); $Shortcut.TargetPath = \"\"\"\\" + list[i][list[i].find("\\"):-1] + "\"\"\"; $Shortcut.Save()"
subprocess.Popen(temp, shell=True)
这会贯穿一个类似于“COMPUTER1/在此处插入 ip ”的列表
然后我有另一个脚本,它使用任务列表来查找 PowerShell 进程并在大约 2 秒后杀死它们。这可行,但会导致内存泄漏。我认为这是杀死 PowerShell 的结果,并且资源没有从 RAM 中释放。
有没有办法释放内存或完全避免泄漏?
如果您在循环中运行它,那么是的..您一定会遇到内存问题,因为您不断创建 COM 对象,但从未从内存中释放它们。
Shortcut.Save()
之后,您需要确保 Windows 垃圾收集器将使用以下方法从内存中删除它们:
$null = [System.Runtime.Interopservices.Marshal]::ReleaseComObject($shortcut)
$null = [System.Runtime.Interopservices.Marshal]::ReleaseComObject($WshShell)
[System.GC]::Collect()
[System.GC]::WaitForPendingFinalizers()
在网络路径不可用时强制终止脚本也是一个坏主意。 为什么不在尝试创建快捷方式之前测试网络路径? 这就是 Powershell cmdlet Test-NetConnection 和 Test-Path 的用途。
顺便说一句将所有代码写在一长行中不仅丑陋,而且也无助于发现您可能在其中放入的任何错误。最好在多行中编写代码并进行适当的缩进。
使用如果您的意思是
tasklist
查找powershell
进程并在大约 2 秒后杀死它们
taskkill.exe
:默认情况下(除非通过了
/F
),它会尝试“合作地”终止进程,即它给目标进程一个机会在终止之前进行清理,甚至允许它“拒绝”终止。 但是,这种协作机制仅适用于 GUI 子系统应用程序,而 PowerShell 是一个 console
Stop-Process
cmdlet always
会强制终止进程,因此本身就存在内存泄漏的风险 - 请参阅 GitHub 问题 #13664 进行讨论。 因此你的方法无法避免内存泄漏。
因此,您需要一种处理超时
进程内的方法,您可以确保正确的清理,这也相当于一个更有效的解决方案,因为您可以使用单个进程: 以下示例代码说明了一种可行的方法。 注:
它依赖于
-only -Parallel
参数 ForEach-Object
中运行操作。在 Windows PowerShell
版本为 5.1),您可以: 安装
Start-ThreadJob
自带的模块,并用它来启动各个线程作业;例如与 Install-Module -Scope CurrentUser ThreadJob
# Create sample input in the form of 'Computer1', 'Computer2', ...
$computersToTest = 1..5 | ForEach-Object { 'Computer' + $_ }
# Launch a parallel thread for each computer.
$jb =
$computersToTest |
ForEach-Object -AsJob -ThrottleLimit $computersToTest.Count -Parallel {
# Simulate a long-running network operation here; e.g.:
# Test-NetConnection $_ -Port 445 -InformationLevel Quiet
Start-Sleep (Get-Random 6)
$_
}
# Check for computers that have responded within a specific timeout period,
# and act on only them.
$sw = [System.Diagnostics.Stopwatch]::StartNew()
while ($sw.Elapsed.TotalSeconds -le 2) {
# Check for any output from the job's child jobs and act on it.
$jb | Receive-Job | ForEach-Object {
"Taking action for $_..."
}
# Sleep a little before checking for job output again.
Start-Sleep -Milliseconds 200
}
# Clean up. This will stop any child jobs that haven't finished yet.
$jb | Remove-Job -Force
上面
使用了轮询循环Receive-Job
检查结果,有点尴尬。
然而,这是
确保处理作业(线程)结果当它们变得可用时
Wait-Job
cmdlet 确实有 -Timeout
参数,但在整个超时期限过去之前,它不会产生输出(然后输出描述已完成作业的job 对象,而不是结果)。
根据设计,使用
-Any
和 $jb.ChildJobs
one已完成的作业(一旦可用)。 虽然
Receive-Job
-Wait
开关,但从 PowerShell 7.4.x 开始,它缺少
-Timeout
参数;如果添加这样的参数,基于超时的等待也会在结果可用时流式传输将成为可能。遗憾的是,为此目的提出的功能请求,GitHub 问题 #5433,被
拒绝了,因为人们认为社区缺乏兴趣。 如果您
Wait-Job -Timeout
与 Receive-Job
结合使用,如下所示:
# ... job-creation code omitted; copy from above.
# Wait for the job(s) to complete, with a timeout.
$timedOut = $null -eq ($jb | Wait-Job -Timeout 3)
# Stop any child jobs that haven't completed yet.
$jb | Stop-Job
# See how many have completed.
$numCompleted = @($jb.ChildJobs.State -eq 'Completed').Count
Write-Verbose -Verbose "$numCompleted of $($jb.ChildJobs.Count) thread(s) completed within the timeout period."
# Process whatever results are available.
$jb | Receive-Job | ForEach-Object {
"Taking action for $_..."
}
# Clean up.
$jb | Remove-Job