PowerShell 和 Net.WebClient。需要澄清工作行为

标签 powershell webclient jobs

假设我正在使用 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/

相关文章:

c# - 使用 webClient C# 在异步文件下载中将文件名传递给 DownloadFileCompleted

html - 使用 powershell 单击网页上的链接,然后传递凭据

xml - 使用未知大小的数组中的项目作为选项在 powershell 中创建菜单

powershell - 如何检查密码复杂性?

c# - 多个 WebClient 不工作?

php - 在网络请求后运行后台进程

python - 从按日期/名称分组的单独目录中的 JPEG 生成 PDF

c# - 在 BackgroundWorker 中取消 Webclient.DownloadFile

node.js - 您如何实现定期执行的工作?

ruby-on-rails - 如何序列化 ActiveModel 错误以供以后翻译?