【问题标题】:PowerShell & Net.WebClient. Need clarification on jobs behaviourPowerShell 和 Net.WebClient。需要澄清工作行为
【发布时间】:2021-04-19 14:02:36
【问题描述】:

假设我正在使用 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 避免在这里开始工作,但我真的很想了解这种机制:)

【问题讨论】:

  • 计算机和软件,虽然是很棒的工具;是愚蠢的(也许顺从是一个更好的词),只会做他们被告知/编程要做的事情。

标签: powershell webclient jobs


【解决方案1】:

我相信这是因为执行已流入 .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 执行的低级工作原理,但是在前两个 sn-ps 中,当父进程关闭时,作业进程正在运行 PowerShell 代码,因此它可以在任意点并响应父进程的终止信号。在第三个 sn-p 中,作业进程正在运行 .NET 代码,等待方法返回。我不能说运行 .NET 代码的线程是与父进程通信的同一线程,还是它是不同的线程,PowerShell 只是尊重the dangers of aborting another thread(PowerShell 中断DownloadFile() 没有问题在作业之外运行时退出建议前者),但结果是相同的:作业进程不会终止,因为它在完成之前“卡”在 .NET 代码中。

这也可能与为什么 Ctrl + C 在执行 .NET 方法时(立即)不起作用有关。见Powershell AcceptTcpClient() cannot be interrupted by Ctrl-C

还有一点:确保在finally 块内调用Dispose(),以确保即使DownloadFile() 抛出异常,它也会被调用...

$wbcl = New-Object System.Net.WebClient
try
{
    $wbcl.DownloadFile($uri1, $localfile1)
}
finally
{
    $wbcl.Dispose()
}

【讨论】:

  • 非常感谢这项研究,先生。您的解释,以及您提供的参考页面上的附加信息,让我明白了。据我了解,这在某种程度上被称为“设计”。
  • PS有几种下载方式。各有利弊。
【解决方案2】:

简单地说,这个……

$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()

.. 不是工作。这是一个交互式会话。关闭/退出会话,您已经终止了活动。

这...

Start-Job -ScriptBlock `
{
  #SAME CODE AS ABOVE
} | Out-Null

#SOME PARALLEL ACTIVITY

Wait-Job -ID 1 | Out-Null

... 是真正的后台作业,从交互式 Powershell 会话开始。

如果您想查看代码调用的内容,请利用...

跟踪命令

https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/trace-command?view=powershell-7.1

Trace-Command -Name metadata,parameterbinding,cmdlet -Expression {
    $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()
} -PSHost

您会注意到,您从上面得到了大量的交互式数据。

# Results
<#
DEBUG: ParameterBinding Information: 0 : BIND NAMED cmd line args [New-Object]
DEBUG: ParameterBinding Information: 0 : BIND POSITIONAL cmd line args [New-Object]
DEBUG: ParameterBinding Information: 0 :     BIND arg [System.Net.WebClient] to parameter [TypeName]
DEBUG: ParameterBinding Information: 0 :         Executing VALIDATION metadata: [System.Management.Automation.ValidateTrustedDataAttribute]
DEBUG: ParameterBinding Information: 0 :         BIND arg [System.Net.WebClient] to param [TypeName] SUCCESSFUL
DEBUG: ParameterBinding Information: 0 : MANDATORY PARAMETER CHECK on cmdlet [New-Object]
DEBUG: ParameterBinding Information: 0 : CALLING BeginProcessing
DEBUG: ParameterBinding Information: 0 : CALLING EndProcessing
DEBUG: ParameterBinding Information: 0 : BIND NAMED cmd line args [Get-Module]
DEBUG: ParameterBinding Information: 0 :     BIND arg [True] to parameter [ListAvailable]
DEBUG: ParameterBinding Information: 0 :         COERCE arg to [System.Management.Automation.SwitchParameter]
DEBUG: ParameterBinding Information: 0 :             Trying to convert argument value from System.Boolean to System.Management.Automation.SwitchParamet
er...

#>

对 Job 执行上述操作,不会返回交互式内容。您必须专门询问工作/细节的状态。

Get-Job
# Results
<#
Id Name PSJobTypeName State     HasMoreData Location  Command 
-- ---- ------------- -----     ----------- --------  ------- 
1  Job1 BackgroundJob Completed True        localhost ... 
#>

或者

Get-Job -Name 'Job1' | 
Select-Object -Property '*' | 
Format-List -Force
# Results
<#
State         : Completed
HasMoreData   : True
StatusMessage : 
Location      : localhost
Command       : 
                    $uri1       = 'http://mirror.internode.on.net/pub/test/10meg.test'
                    $localfile1 = "$Env:userprofile\Downloads\10meg.test"
                
                    $wbcl = New-Object System.Net.WebClient
                    $wbcl.DownloadFile($uri1, $localfile1)
                    $wbcl.Dispose()
            
                    #SOME PARALLEL ACTIVITY
                    Wait-Job -ID 1 | 
                    Out-Null
            
JobStateInfo  : Completed
Finished      : System.Threading.ManualResetEvent
InstanceId    : 1af73ea0-c0bf-4cc1-b637-71b0e48862bc
Id            : 1
Name          : Job1
ChildJobs     : {Job2}
PSBeginTime   : 18-Apr-21 21:29:44
PSEndTime     : 18-Apr-21 21:29:46
PSJobTypeName : BackgroundJob
Output        : {}
Error         : {}
Progress      : {}
Verbose       : {}
Debug         : {}
Warning       : {}
Information   : {}
#>

根据我对下载方式的评论:

https://blog.jourdant.me/post/3-ways-to-download-files-with-powershell

  1. 调用-WebRequest

缺点

速度。此 cmdlet 很慢。据我观察,HTTP 响应流被缓冲到内存中。一旦文件已完全 加载,它被刷新到磁盘。这增加了巨大的性能损失,并且 大文件的潜在内存问题。如果有人知道具体情况 这个 cmdlet 是如何运作的,请告诉我!

此方法的另一个潜在严重缺陷是依赖于 IE浏览器。例如,此 cmdlet 不能在 Windows 上使用 服务器核心版服务器作为 Internet Explorer 二进制文件不是 默认包含。在某些情况下,您可以使用 -UseBasicParsing 参数,但并非在所有情况下都有效。

  1. System.Net.WebClient

用于下载文件的常见 .NET 类是 System.Net.WebClient 类。

缺点

没有可见的进度指示器(或任何查询 进度中转)。它基本上阻塞了线程,直到 下载完成或失败。然而,这不是一个主要的骗局, 有时可以很方便地知道您的传输距离。

  1. 起始位传输

如果您以前没有听说过 BITS,请查看此内容。 BITS 主要是 专为异步文件下载而设计,但运行良好 也同步(假设您启用了 BITS)。

缺点

虽然在许多机器上默认启用 BITS,但您不能保证 它全部启用(除非您正在积极管理它)。还 使用 BITS 的设计方式,如果其他 BITS 作业正在运行 背景,您的工作可能会排队或稍后运行 脚本的执行。

【讨论】:

  • 所以基本上,根据我的新手理解,这项工作是另一种具有自己内部结构的活动?不是直接的 session-inside-session 结构。
  • 详细信息About Jobs | MSDOcs PowerShell 通过作业同时运行命令和脚本。 PowerShell 提供了三种作业类型来支持并发。 1. RemoteJob - 命令和脚本在远程会话上运行。有关信息,请参阅 about_Remote_Jobs。 --- 2. BackgroundJob - 命令和脚本在本地机器上的单独进程中运行。 --- 3. PSTaskJob 或 ThreadJob - 命令和脚本在本地机器上同一进程内的单独线程中运行。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-08-04
相关资源
最近更新 更多