Multithreading 使用后台脚本块调试流水线powershell函数的意外结果

Multithreading 使用后台脚本块调试流水线powershell函数的意外结果,multithreading,powershell,Multithreading,Powershell,一旦我启用从管道中提取值,我就要处理powershell函数的意外结果。该函数使用主机名列表并检索有关缺少更新的信息。将主机名作为数组(第二个代码示例)并迭代以构建后台任务集时,该函数按预期工作。一旦我重构以使用管道,我就会遇到某种冲突,我认为这与范围或线程安全有关——每个查询的结果都是为本地主机而不是指定的主机生成的 我花了一天多的时间研究和排除故障,但没有任何不同的结果。我试过: 在scriptblock和函数中更改变量/参数名称 在开始和进程之间移动脚本块 通过关闭scriptblock

一旦我启用从管道中提取值,我就要处理powershell函数的意外结果。该函数使用主机名列表并检索有关缺少更新的信息。将主机名作为数组(第二个代码示例)并迭代以构建后台任务集时,该函数按预期工作。一旦我重构以使用管道,我就会遇到某种冲突,我认为这与范围或线程安全有关——每个查询的结果都是为本地主机而不是指定的主机生成的

我花了一天多的时间研究和排除故障,但没有任何不同的结果。我试过:

  • 在scriptblock和函数中更改变量/参数名称
  • 在开始和进程之间移动脚本块
  • 通过关闭scriptblock创建后台任务
  • 创建独立的运行空间而不是运行空间池
  • 从输出数据收集器与EndInvoke检索结果
  • 在ISE之外运行
  • 端块的几个版本
我还添加了各种调试语句并修改了返回值,以确认进程块和脚本块都接收到管道版本中预期的不同主机名

有谁能提供我在Powershell的范围界定和线程安全方面的误解

注意:我已禁用连接测试,以便在通过无效主机时强制脚本块中出现异常

流水线版本 阵列版本
如果
AddParameter(“A”、$ComputerName)
将更改为
AddParameter(“A”、$ComputerName”)
AddParameter(“A”、$ComputerName[0])
,管道版本将如何操作?如果
AddParameter(“A”、$ComputerName)
将更改为
AddParameter(“A”,“$ComputerName”)
添加参数(“A”,$ComputerName[0])
function Get-UpdateDetail {
    [CmdletBinding()]
    param (
        [Parameter(ValueFromPipeline=$True)]
        [string[]]$ComputerName,
        [string]$Criteria = "IsHidden=0 and IsInstalled=0"
    )
    Begin {

        $RunspacePool = [RunspaceFactory]::CreateRunspacePool(1,10)
        $RunspacePool.Open()
        $Jobs = @() 
        $Results = @()  
    }
    Process {

        Write-Verbose "Connecting to $ComputerName"

        if(1) { #Test-Connection -ComputerName $ComputerName -count 1 -quiet) {

            $Thread = [PowerShell]::Create().AddScript({
                param ($A, $B)
                Try {

                    [activator]::CreateInstance([type]::GetTypeFromProgID("Microsoft.Update.Session", $A)).CreateUpdateSearcher().Search($B)
                } Catch {
                    $_.Exception
                }
            }).AddParameter("A",$ComputerName).AddParameter("B",$Criteria)

            $Thread.RunspacePool = $RunspacePool

            $Jobs += New-Object PSObject -Property @{
                Host = $ComputerName
                Thread = $Thread
                Handle = $Thread.BeginInvoke()
            }
        }
        else {
            $Results += New-Object PSObject -Property @{
                Host = $ComputerName
                Results = "Offline"
            }
        }
    }
    End {

        While ( $Jobs.Handle.IsCompleted -contains $false) {

            $Remaining = $($Jobs | Where-Object {$_.Handle.IsCompleted -eq $False}).count
            $Complete = ($Jobs.count - $Remaining)/$Jobs.count * 100
            Write-Progress `
                -Activity "Waiting for remaining jobs to complete ..." `
                -PercentComplete $Complete

            ForEach ($Job in $($Jobs | Where-Object {$_.Handle.IsCompleted -eq $True})) {
                Write-Progress `
                    -Activity "Waiting for remaining jobs to complete ..." `
                    -Status "Finishing background job for $($Job.Host)" `
                    -PercentComplete $Complete

                $Results += New-Object PSObject -Property @{
                    Host = $Job.Host
                    Thread = $Job.Thread
                    Handle = $Job.Handle
                    Results = $Job.Thread.EndInvoke($Job.Handle)
                }
                $Job.Thread.Dispose()
                $Job.Thread = $Null
                $Job.Handle = $Null
            }
            Start-Sleep -Seconds 1
        }

        $RunspacePool.Close()
        $RunspacePool.Dispose()
        $Results
    }
}
function Get-UpdateDetail {
    [CmdletBinding()]
    param (
        [Parameter()]
        [string[]]$ComputerName,
        [string]$Criteria = "IsHidden=0 and IsInstalled=0"
    )


    $RunspacePool = [RunspaceFactory]::CreateRunspacePool(1,10)
    $RunspacePool.Open()
    $Jobs = @() 
    $Results = @()  

    ForEach ($Computer in $ComputerName) {

        Write-Verbose "Connecting to $Computer"

        if(1) { Test-Connection -ComputerName $Computer -count 1 -quiet) {

            $Thread = [PowerShell]::Create().AddScript({
                param ($A, $B)
                Try {

                    [activator]::CreateInstance([type]::GetTypeFromProgID("Microsoft.Update.Session", $A)).CreateUpdateSearcher().Search($B)
                } Catch {
                    $_.Exception
                }
            }).AddParameter("A",$Computer).AddParameter("B",$Criteria)

            $Thread.RunspacePool = $RunspacePool

            $Jobs += New-Object PSObject -Property @{
                Host = $Computer
                Thread = $Thread
                Handle = $Thread.BeginInvoke()
            }
        }
        else {
            $Results += New-Object PSObject -Property @{
                Host = $Computer
                Results = "Offline"
            }
        }
    }

    While ( $Jobs.Handle.IsCompleted -contains $false) {

        $Remaining = $($Jobs | Where-Object {$_.Handle.IsCompleted -eq $False}).count
        $Complete = ($Jobs.count - $Remaining)/$Jobs.count * 100
        Write-Progress `
            -Activity "Waiting for remaining jobs to complete ..." `
            -PercentComplete $Complete

        ForEach ($Job in $($Jobs | Where-Object {$_.Handle.IsCompleted -eq $True})) {
            Write-Progress `
                -Activity "Waiting for remaining jobs to complete ..." `
                -Status "Finishing background job for $($Job.Host)" `
                -PercentComplete $Complete

            $Results += New-Object PSObject -Property @{
                Host = $Job.Host
                Thread = $Job.Thread
                Handle = $Job.Handle
                Results = $Job.Thread.EndInvoke($Job.Handle)
            }
            $Job.Thread.Dispose()
            $Job.Thread = $Null
            $Job.Handle = $Null
        }
        Start-Sleep -Seconds 1
    }

    $RunspacePool.Close()
    $RunspacePool.Dispose()
    $Results

}