如何在 PowerShell 中使用 ForEach-Object Parallel cmdlet?


Foreach-Object -Parallel 命令是在 PowerShell 7 预览版 3 中引入的,它用于并行执行管道输入,本文将对此进行更详细的解释。

请注意:Foreach-Object 和 Foreach 命令相同,但我们不能写 Foreach -Parallel 命令,因为没有这样的命令,我们需要使用完整的命令 Foreach-Object -Parallel

运行并行对象的简单示例。

示例

1..10 | ForEach-Object -Parallel{
    $_ * 1000  
    Sleep 1
}

输出

1000
2000
3000
4000
5000
6000
7000
8000
9000
10000

让我们比较使用和不使用 -Parallel 参数的计时。

并行执行

$time1 = Measure-Command {
    1..10 | ForEach-Object -Parallel{
        $_ * 1000  
        Sleep 1
    }

}
    
Write-Output "Parallel Time : Minutes : $($time1.Minutes) , Seconds : $($time1.Seconds) , Milliseconds : $($time1.TotalMilliseconds) "

输出

Parallel Time : Minutes : 0 , Seconds : 2 , Milliseconds : 2500.9792 

顺序执行

$time1 = Measure-Command {
    1..10 | ForEach-Object {
        $_ * 1000  
        Sleep 1
    }

}
    
Write-Output "Sequential Time : Minutes : $($time1.Minutes) , Seconds : $($time1.Seconds) , Milliseconds : $($time1.TotalMilliseconds) "

输出

Sequential Time : Minutes : 0 , Seconds : 10 , Milliseconds : 10062.0786

您可以看到以上两种执行之间的显著差异,并行运行比顺序运行快得多。

还有一个参数支持 foreach-Object 循环的并行化,即 -ThrottleLimit,表示并行执行的线程数。默认情况下,节流限制为 5。请查看增加节流限制后的时间差异。

示例

$time1 = Measure-Command {
    1..10 | ForEach-Object -Parallel{
        $_ * 1000  
        Sleep 1
    } -ThrottleLimit 10

}
    
Write-Output "Sequential Time : Minutes : $($time1.Minutes) , Seconds : $($time1.Seconds) , Milliseconds : $($time1.TotalMilliseconds) "

输出

Parallel Time : Minutes : 0 , Seconds : 1 , Milliseconds : 1615.4921 

如果您比较第一个并行时间示例,第二个更快,因为我们增加了节流限制。当您增加节流限制时,请确保哪个命令用于并行执行,以及有多少节流限制作为输入传递,否则系统将因脚本执行而超载,因为生成了许多 PowerShell 线程。

您也可以将此命令作为后台作业运行,因为它支持 -AsJob 参数。

$fjob = 1..10 | ForEach-Object -Parallel{
        $_ * 1000  
        Sleep 1
} -ThrottleLimit 10 -AsJob 

$fjob | Receive-Job

其他远程命令和 foreach-Object -Parallel 之间的区别在于,此命令在同一上下文中并行运行作业,这称为 命名空间,而其他远程命令则使用 -ComputerName 参数或 Invoke-Command 在远程计算机上顺序运行,这就是并行执行对于远程计算机快得多的原因。

当您在 PS 版本 7 中使用并行 foreach 循环时,并行循环内唯一可见的变量是管道变量,其他外部变量或对象可以使用 Using: 关键字访问。例如:

示例

$computers = "Test1-Win2k12", "test1-Win2k16"
$command = "Win32_OperatingSystem"

$computers | ForEach-Object -Parallel {
   Get-CimInstance $using:command -ComputerName $_
}

您可以在上面的脚本中看到,只有 $computers 可以通过 $_ 在 Foreach 循环中访问,但要访问 $command,我们需要使用 $using:Command,因为它在循环的作用域之外,但这在简单的 foreach 循环中并非如此。

更新于:2020年11月11日

2K+ 次查看

启动您的 职业生涯

完成课程获得认证

开始学习
广告