假设我正在使用 Net.WebClient 的 DownloadFile 方法下载大文件:
$uri1 = "blabla.com/distro/blabla_2gb.exe"
$localfile1 = "$Env:userprofile\Downloads\blabla_2gb.exe"
$wbcl = New-Object System.Net.WebClient
$wbcl.DownloadFile($uri1, $localfile1)
$wbcl.Dispose()
在这种情况下,我可以随时使用 Alt + F4 之类的命令终止我的脚本。下载过程将停止,$wbcl 将被自动释放。
但如果我在工作中做同样的事情:
Start-Job -ScriptBlock `
{
#SAME CODE AS ABOVE
} | Out-Null
#SOME PARALLEL ACTIVITY
Wait-Job -ID 1 | Out-Null
即使父脚本关闭,下载也会继续。根据文档,父脚本的终止将导致所有相应作业的停止。那为什么还继续下载呢?
附注我知道我可以通过使用 DownloadFileAsync 避免在这里开始工作,但我真的很想了解这种机制:)
最佳答案
我相信这是因为执行已流入 .NET 方法,而 PowerShell 不再对其进行控制。
例如,如果我运行...
Start-Job -ScriptBlock { Start-Sleep -Seconds 30 }
...或者...
Start-Job -ScriptBlock { while ($true) { } }
...我可以在任务管理器中看到有两个 PowerShell 进程。如果我单击 PowerShell 窗口的关闭按钮(Alt + F4 对我来说不起作用),两个进程都会立即消失。
如果我跑...
Start-Job -ScriptBlock { [System.Threading.Thread]::Sleep([TimeSpan]::FromSeconds(30)) }
...然后我还在任务管理器中看到两个 PowerShell 进程。然而,关闭PowerShell窗口后,只有一个PowerShell进程立即消失;另一个在剩余 30 秒后消失。有趣的是,如果我运行 exit
而不是关闭 PowerShell 窗口,该窗口将保持打开状态并闪烁光标,直到作业完成。
观察这一情况的另一种方法是使用 Stop-Job
。在此脚本中...
$job = Start-Job -ScriptBlock { Start-Sleep -Seconds 30 }
Start-Sleep -Seconds 1 # Give the job time to transition to the Running state
$job | Stop-Job
...Stop-Job
立即返回,而在此脚本中...
$job = Start-Job -ScriptBlock { [System.Threading.Thread]::Sleep([TimeSpan]::FromSeconds(30)) }
Start-Sleep -Seconds 1 # Give the job time to transition to the Running state
$job | Stop-Job
...需要 30 秒。
我不太熟悉 PowerShell 执行的低级工作原理,但在前两个片段中,当父进程关闭时,作业进程正在运行 PowerShell 代码,因此它将能够在任意点中断并响应父进程的终止信号。在第三个片段中,作业进程正在运行 .NET 代码,等待方法返回。我不能说运行 .NET 代码的线程是否是与父进程通信的同一线程,或者它是一个不同的线程,而 PowerShell 只是尊重 the dangers of aborting another thread (PowerShell 在作业外运行时可以毫无问题地中断 DownloadFile()
退出,这表明是前者),但结果是相同的:作业进程不会终止,因为它“卡住”了在 .NET 代码中直至完成。
这也可能与为什么 Ctrl + C 在执行 .NET 方法时不能(立即)工作有关。请参阅Powershell AcceptTcpClient() cannot be interrupted by Ctrl-C .
另一点:确保在 finally
block 内调用 Dispose()
,以确保即使 DownloadFile()
也确实会调用它抛出异常...
$wbcl = New-Object System.Net.WebClient
try
{
$wbcl.DownloadFile($uri1, $localfile1)
}
finally
{
$wbcl.Dispose()
}
关于PowerShell 和 Net.WebClient。需要澄清工作行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/67143426/