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