Seit PowerShell 7.0 kann man mit dem Parameter -parallel
die Ausführung der Schleifendurchläufe auf verschiedene Threads parallelisieren (via Multithreading).
Im ersten Teil dieser Blogserie habe ich die technische Basis für die PowerShell 7.0 betrachtet, im zweiten Teil den aktuellen Funktionsumfang der PowerShell 7.0 im Vergleich zur PowerShell Core 6.x und Windows PowerShell 5.1 quantifiziert. Nun möchte ich damit beginnen, einzelne neue Funktionen vorzustellen. Dabei geht es hier um den mächtigen neuen Parameter -parallel
im altbekannten Commandlet Foreach-Object
.
In PowerShell 1.0 bis 6.2 erfolgt die Ausführung von Foreach-Object
im Haupt-Thread der PowerShell, das heißt, die einzelnen Durchläufe erfolgen nacheinander. Seit PowerShell 7.0 kann man mit dem Parameter -parallel
die Ausführung auf verschiedene Threads parallelisieren (via Multithreading), sodass bei längeren Operationen in Summe das Ergebnis schneller vorliegt.
Multithreading hat immer einigen Overhead. Die Parallelisierung lohnt sich nur bei länger dauernden Operationen. Bei kurzen Operationen ist der Zeitverlust durch das Erzeugen und Vernichten der Threads höher als der Zeitgewinn durch die Parallelisierung.
Das folgende Beispiel zeigt zwei Varianten der Abfrage, ob die Software "Classic Shell" auf drei verschiedenen Computern installiert ist. Bei der ersten Variante ohne -parallel
wird die leider etwas langwierige Abfrage der WMI-Klasse Win32_Product
auf den drei Computern nacheinander im gleichen Thread ausgeführt. Bei der zweiten Variante mit -parallel
wird die Abfrage parallel in drei verschiedenen Threads gestartet; die Ergebnisse kommen erst rein, nachdem alle Anfragen raus sind (s. Abb. unten). Dass die Ausführung tatsächlich in verschiedene Threads erfolgt, wird bewiesen unter Abfrage der ManagedThreadId
im aktuellen Thread aus der .NET-Klasse Thread: [System.Threading.Thread]::CurrentThread.ManagedThreadId
.
Write-Host "# ForEach-Object ohne -parallel" -ForegroundColor Yellow
"E27","E29","E44" | ForEach-Object {
"Abfrage bei Computer $_ in Thread $([System.Threading.Thread]::CurrentThread.ManagedThreadId)"
$e = Get-CimInstance -Class Win32_Product -Filter "Name='Classic Shell'" -computername $_
if ($e -eq $null) { "Kein Ergebnis bei $_!"}
else { $e }
}
Write-host ""
Write-host " # ForEach-Object mit -parallel" -ForegroundColor Yellow
"E27","E29","E44" | ForEach-Object -parallel {
"Abfrage bei Computer $_ in Thread $([System.Threading.Thread]::CurrentThread.ManagedThreadId)"
$e = Get-CimInstance -Class Win32_Product -Filter "Name='Classic Shell'" -computername $_
if ($e -eq $null) { "Kein Ergebnis bei $_!"}
else { $e }
}
# ohne Read-Host würde das Skript die später eingehenden Ergebnisse nicht mehr anzeigen!
read-host
Parallelität bei Foreach-Object in PowerShell 7
Die Anzahl der Threads, die Foreach-Object
nutzen soll, kann man mit dem Parameter -ThrottleLimit
begrenzen:
1..20 | ForEach-Object -parallel {
Write-host "Objekt #$_ in Thread $([System.Threading.Thread]::CurrentThread.ManagedThreadId)"
sleep -Seconds 2 } -ThrottleLimit 5
()