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

在PowerShell版本7(预览版3)中引入了Foreach-Object -Parallel命令,命令用于并行执行管道输入,本文对此进行了详细说明。

请注意: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中使用Parallel 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循环。