about_Thread_Jobs
Kurze Beschreibung
Stellt Informationen zu Thread-basierten PowerShell-Aufträgen bereit. Ein Threadauftrag ist ein Hintergrundauftragstyp, der einen Befehl oder Ausdruck in einem separaten Thread innerhalb des aktuellen Sitzungsprozesses ausführt.
Lange Beschreibung
PowerShell führt Befehle und Skripts gleichzeitig über Aufträge aus. Es gibt drei Auftragstypen, die von PowerShell bereitgestellt werden, um Parallelität zu unterstützen.
RemoteJob
– Befehle und Skripts werden in einer Remotesitzung ausgeführt. Weitere Informationen finden Sie unter about_Remote_Jobs.BackgroundJob
– Befehle und Skripts werden in einem separaten Prozess auf dem lokalen Computer ausgeführt. Weitere Informationen finden Sie unter about_Jobs.PSTaskJob
oderThreadJob
- Befehle und Skripts werden in einem separaten Thread innerhalb desselben Prozesses auf dem lokalen Computer ausgeführt.
Threadbasierte Aufträge sind nicht so robust wie Remote- und Hintergrundaufträge, da sie im selben Prozess in verschiedenen Threads ausgeführt werden. Wenn ein Auftrag einen kritischen Fehler aufweist, der den Prozess abstürzt, werden alle anderen Aufträge im Prozess beendet.
Threadbasierte Aufträge erfordern jedoch weniger Aufwand. Sie verwenden nicht die Remotingschicht oder Serialisierung. Die Ergebnisobjekte werden als Verweise auf Liveobjekte in der aktuellen Sitzung zurückgegeben. Ohne diesen Aufwand werden threadbasierte Aufträge schneller ausgeführt und verwenden weniger Ressourcen als die anderen Auftragstypen.
Wichtig
Die übergeordnete Sitzung, die den Auftrag erstellt hat, überwacht auch den Auftragsstatus und sammelt Pipelinedaten. Der untergeordnete Auftragsvorgang wird vom übergeordneten Prozess beendet, sobald der Auftrag einen abgeschlossenen Zustand erreicht hat. Wenn die übergeordnete Sitzung beendet wird, werden alle ausgeführten untergeordneten Aufträge zusammen mit ihren untergeordneten Prozessen beendet.
Es gibt zwei Möglichkeiten, diese Situation zu umgehen:
- Dient
Invoke-Command
zum Erstellen von Aufträgen, die in getrennten Sitzungen ausgeführt werden. Weitere Informationen finden Sie unter about_Remote_Jobs. - Wird
Start-Process
verwendet, um einen neuen Prozess anstelle eines Auftrags zu erstellen. Weitere Informationen finden Sie unter Start-Process.
So starten und verwalten Sie threadbasierte Aufträge
Es gibt zwei Möglichkeiten, threadbasierte Aufträge zu starten:
Start-ThreadJob
- aus dem ThreadJob-ModulForEach-Object -Parallel -AsJob
– Das parallele Feature wurde in PowerShell 7.0 hinzugefügt.
Verwenden Sie die in about_Jobs beschriebenen Cmdlets "Auftrag" zum Verwalten von threadbasierten Aufträgen.
Verwenden von Start-ThreadJob
Das ThreadJob-Modul wurde zuerst mit PowerShell 6 ausgeliefert. Sie kann auch über die PowerShell-Katalog für Windows PowerShell 5.1 installiert werden.
Um einen Threadauftrag auf dem lokalen Computer zu starten, verwenden Sie das Cmdlet mit einem Befehl oder Skript, der Start-ThreadJob
in geschweifte geschweifte Klammern ({ }
) eingeschlossen ist.
Im folgenden Beispiel wird ein Threadauftrag gestartet, der einen Get-Process
Befehl auf dem lokalen Computer ausführt.
Start-ThreadJob -ScriptBlock { Get-Process }
Der Start-ThreadJob
Befehl gibt ein ThreadJob
Objekt zurück, das den ausgeführten Auftrag darstellt. Das Auftragsobjekt enthält nützliche Informationen zum Auftrag, einschließlich des aktuellen Ausführungsstatus. Es sammelt die Ergebnisse des Auftrags, während die Ergebnisse generiert werden.
Verwenden von ForEach-Object -Parallel -AsJob
PowerShell 7.0 hat dem Cmdlet einen neuen Parameter hinzugefügt ForEach-Object
. Mit den neuen Parametern können Sie Skriptblöcke in parallelen Threads als PowerShell-Aufträge ausführen.
Sie können Daten an ForEach-Object -Parallel
. Die Daten werden an den Skriptblock übergeben, der parallel ausgeführt wird. Der -AsJob
Parameter erstellt Auftragsobjekte für jeden der parallelen Threads.
Der folgende Befehl startet einen Auftrag, der untergeordnete Aufträge für jeden Eingabewert enthält, der an den Befehl weitergeleitet wird. Jeder untergeordnete Auftrag führt den Write-Output
Befehl mit einem Weitergeleiteten Eingabewert als Argument aus.
1..5 | ForEach-Object -Parallel { Write-Output $_ } -AsJob
Der ForEach-Object -Parallel
Befehl gibt ein PSTaskJob
Objekt zurück, das untergeordnete Aufträge für jeden Weitergeleiteten Eingabewert enthält. Das Auftragsobjekt enthält nützliche Informationen zu den untergeordneten Aufträgen, die ausgeführt werden. Es sammelt die Ergebnisse der untergeordneten Aufträge, während die Ergebnisse generiert werden.
Warten auf den Abschluss eines Auftrags und Abrufen von Auftragsergebnissen
Sie können PowerShell-Auftrags-Cmdlets verwenden, z Wait-Job
. B. und Receive-Job
warten, bis ein Auftrag abgeschlossen ist, und dann alle Vom Auftrag generierten Ergebnisse zurückgeben.
Der folgende Befehl startet einen Threadauftrag, der einen Get-Process
Befehl ausführt, wartet dann, bis der Befehl abgeschlossen ist, und gibt schließlich alle vom Befehl generierten Datenergebnisse zurück.
Start-ThreadJob -ScriptBlock { Get-Process } | Wait-Job | Receive-Job
Der folgende Befehl startet einen Auftrag, der einen Write-Output
Befehl für jede weitergeleitete Eingabe ausführt, dann wartet, bis alle untergeordneten Aufträge abgeschlossen sind, und gibt schließlich alle Datenergebnisse zurück, die von den untergeordneten Aufträgen generiert wurden.
1..5 | ForEach-Object -Parallel { Write-Output $_ } -AsJob | Wait-Job | Receive-Job
Das Receive-Job
Cmdlet gibt die Ergebnisse der untergeordneten Aufträge zurück.
1
3
2
4
5
Da jeder untergeordnete Auftrag parallel ausgeführt wird, wird die Reihenfolge der generierten Ergebnisse nicht garantiert.
Threadauftragsleistung
Threadaufträge sind schneller und leichter als andere Arten von Aufträgen. Aber sie haben immer noch Mehraufwand, der im Vergleich zur Arbeit, die der Job tut, groß sein kann.
PowerShell führt Befehle und Skripts in einer Sitzung aus. In einer Sitzung kann jeweils nur ein Befehl oder Skript ausgeführt werden. Wenn Sie also mehrere Aufträge ausführen, wird jeder Auftrag in einer separaten Sitzung ausgeführt. Jede Sitzung trägt zum Aufwand bei.
Threadaufträge bieten die beste Leistung, wenn die ausgeführte Arbeit größer ist als der Aufwand der Sitzung, die zum Ausführen des Auftrags verwendet wird. Es gibt zwei Fälle, für die diese Kriterien erfüllt sind.
Die Arbeit ist rechenintensiv– Das Ausführen eines Skripts auf mehreren Threadaufträgen kann mehrere Prozessorkerne nutzen und schneller abschließen.
Die Arbeit besteht aus erheblichen Wartezeiten – ein Skript, das Zeit mit dem Warten auf E/A- oder Remoteanrufergebnisse verbringt. Die Parallelausführung erfolgt in der Regel schneller als bei sequenzieller Ausführung.
(Measure-Command {
1..1000 | ForEach { Start-ThreadJob { Write-Output "Hello $using:_" } } | Receive-Job -Wait
}).TotalMilliseconds
36860.8226
(Measure-Command {
1..1000 | ForEach-Object { "Hello: $_" }
}).TotalMilliseconds
7.1975
Das erste Beispiel oben zeigt eine Foreachschleife, die 1000 Threadaufträge erstellt, um einen einfachen Zeichenfolgenschreibvorgang zu erstellen. Aufgrund des Auftragsaufwands dauert es mehr als 36 Sekunden, bis er abgeschlossen ist.
Im zweiten Beispiel wird das ForEach
Cmdlet ausgeführt, um dieselben 1000 Vorgänge auszuführen.
Diese Zeit ForEach-Object
wird sequenziell auf einem einzelnen Thread ausgeführt, ohne dass ein Auftragsaufwand erforderlich ist. Es wird in nur 7 Millisekunden fertiggestellt.
Im folgenden Beispiel werden bis zu 5000 Einträge für 10 separate Systemprotokolle gesammelt. Da das Skript das Lesen einer Reihe von Protokollen umfasst, ist es sinnvoll, die Vorgänge parallel auszuführen.
$logNames.count
10
Measure-Command {
$logs = $logNames | ForEach-Object {
Get-WinEvent -LogName $_ -MaxEvents 5000 2>$null
}
}
TotalMilliseconds : 252398.4321 (4 minutes 12 seconds)
$logs.Count
50000
Das Skript wird in der Hälfte der Zeit abgeschlossen, in der die Aufträge parallel ausgeführt werden.
Measure-Command {
$logs = $logNames | ForEach {
Start-ThreadJob {
Get-WinEvent -LogName $using:_ -MaxEvents 5000 2>$null
} -ThrottleLimit 10
} | Wait-Job | Receive-Job
}
TotalMilliseconds : 115994.3 (1 minute 56 seconds)
$logs.Count
50000
Threadaufträge und Variablen
Es gibt mehrere Möglichkeiten, Werte an threadbasierte Aufträge zu übergeben.
Start-ThreadJob
kann Variablen akzeptieren, die an das Cmdlet weitergeleitet werden, über das $using
Schlüsselwort an den Skriptblock übergeben oder über den ArgumentList-Parameter übergeben werden.
$msg = "Hello"
$msg | Start-ThreadJob { $input | Write-Output } | Wait-Job | Receive-Job
Start-ThreadJob { Write-Output $using:msg } | Wait-Job | Receive-Job
Start-ThreadJob { param ([string] $message) Write-Output $message } -ArgumentList @($msg) |
Wait-Job | Receive-Job
ForEach-Object -Parallel
akzeptiert variablen und Variablen, die über das $using
Schlüsselwort direkt an den Skriptblock übergeben werden.
$msg = "Hello"
$msg | ForEach-Object -Parallel { Write-Output $_ } -AsJob | Wait-Job | Receive-Job
1..1 | ForEach-Object -Parallel { Write-Output $using:msg } -AsJob | Wait-Job | Receive-Job
Da Threadaufträge im selben Prozess ausgeführt werden, muss jeder variable Verweistyp, der an den Auftrag übergeben wird, sorgfältig behandelt werden. Wenn es sich nicht um ein threadsicheres Objekt handelt, sollte es nie zugewiesen werden, und Methoden und Eigenschaften sollten niemals darauf aufgerufen werden.
Im folgenden Beispiel wird ein threadsicheres .NET-Objekt ConcurrentDictionary
an alle untergeordneten Aufträge übergeben, um eindeutig benannte Prozessobjekte zu sammeln. Da es sich um ein threadsicheres Objekt handelt, kann es sicher verwendet werden, während die Aufträge gleichzeitig im Prozess ausgeführt werden.
$threadSafeDictionary = [System.Collections.Concurrent.ConcurrentDictionary[string,object]]::new()
$jobs = Get-Process | ForEach {
Start-ThreadJob {
$proc = $using:_
$dict = $using:threadSafeDictionary
$dict.TryAdd($proc.ProcessName, $proc)
}
}
$jobs | Wait-Job | Receive-Job
$threadSafeDictionary.Count
96
$threadSafeDictionary["pwsh"]
NPM(K) PM(M) WS(M) CPU(s) Id SI ProcessName
------ ----- ----- ------ -- -- -----------
112 108.25 124.43 69.75 16272 1 pwsh