PowerShell问题:使用ForEach-Object -Parallel后变量为何为空?
解决ForEach-Object -Parallel中变量无法跨运行空间更新的问题
你遇到的问题核心原因是:ForEach-Object -Parallel的每个脚本块都运行在独立的PowerShell运行空间中,主脚本里定义的$DBInventory和并行块内的$DBInventory并非同一个变量,并行块内的修改不会同步到主脚本的变量上。
解决方案1:收集并行块的输出(推荐)
利用PowerShell的管道特性,让每个并行块直接输出结果,主脚本将这些输出统一收集到变量中,这是最符合PowerShell设计模式的方式:
$DBDetails = "SELECT @@VERSION" $VMs = ("vm1", "vm2", "vm3", "vm4", "vm5", "vm6", "vm7") $scriptBlock = { $vm = $_ $result = Invoke-Sqlcmd -ServerInstance $vm -Query $using:DBDetails Write-Host "Added $($result.Count) rows from $($vm)" # 将查询结果输出,供主脚本收集 $result } # 直接接收并行块的所有输出 $DBInventory = $VMs | ForEach-Object -Parallel $scriptBlock Write-Host "Number of elements in DBInventory: $($DBInventory.Count)"
解决方案2:使用引用类型共享状态(适合复杂场景)
如果需要在并行块之间共享状态,可以使用哈希表(引用类型)配合$using:关键字传递,修改哈希表内的内容会同步到主脚本的对象中(注意添加锁避免并发冲突):
$DBDetails = "SELECT @@VERSION" $VMs = ("vm1", "vm2", "vm3", "vm4", "vm5", "vm6", "vm7") # 用哈希表存储结果,哈希表是引用类型 $DBInventory = @{ Data = @() } $scriptBlock = { $vm = $_ $result = Invoke-Sqlcmd -ServerInstance $vm -Query $using:DBDetails # 加锁避免多线程同时修改导致的异常 lock ($using:DBInventory) { $using:DBInventory.Data += $result } Write-Host "Added $($result.Count) rows from $($vm)" } $VMs | ForEach-Object -Parallel $scriptBlock Write-Host "Number of elements in DBInventory: $($DBInventory.Data.Count)"
优先推荐第一种方案,它更简洁高效,无需处理线程安全问题,完全契合PowerShell的管道输出理念。
内容的提问来源于stack exchange,提问作者Arthur Quenneville
相关产品推荐
相关产品推荐

