about_Thread_Jobs

Kurze Beschreibung

Stellt Informationen zu PowerShell-Threadbasierten 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 gleichzeitig Befehle und Skripts über Aufträge aus. Es gibt drei Aufgabentypen, die von PowerShell bereitgestellt werden, um die 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 oder ThreadJob – 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 gleichen Prozess auf 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 Remotingebene 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 Vorgang des untergeordneten Auftrags 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, um diese Situation zu umgehen:

  1. Verwenden Sie Invoke-Command zum Erstellen von Aufträgen, die in getrennten Sitzungen ausgeführt werden. Weitere Informationen finden Sie unter about_Remote_Jobs.
  2. Wird verwendet Start-Process , um einen neuen Prozess anstelle eines Auftrags zu erstellen. Weitere Informationen finden Sie unter "Startprozess".

So starten und verwalten Sie Threadbasierte Aufträge

Es gibt zwei Möglichkeiten zum Starten von Thread-basierten Aufträgen:

  • Start-ThreadJob- aus dem ThreadJob-Modul
  • ForEach-Object -Parallel -AsJob - Das parallele Feature wurde in PowerShell 7.0 hinzugefügt.

Verwenden Sie die in about_Jobs beschriebenen Job-Cmdlets, um Threadbasierte Aufträge zu verwalten.

Verwenden von Start-ThreadJob

Das ThreadJob-Modul wurde zuerst mit PowerShell 6 ausgeliefert. Sie kann auch aus dem PowerShell-Katalog für Windows PowerShell 5.1 installiert werden.

Um einen Threadauftrag auf dem lokalen Computer zu starten, verwenden Sie das Start-ThreadJob Cmdlet mit einem Befehl oder Skript, der in geschweifte Geschweifte ({ }) 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. Er sammelt die Ergebnisse des Auftrags, da 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 jede 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. Sie sammelt die Ergebnisse der untergeordneten Aufträge, da die Ergebnisse generiert werden.

Warten auf einen Auftrag zum Abschließen 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 Ergebnisse zurückgeben, die vom Auftrag generiert werden.

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 auf alle untergeordneten Aufträge wartet, bis alle untergeordneten Aufträge abgeschlossen sind, und gibt schließlich alle Datenergebnisse zurück, die von den untergeordneten Aufträgen generiert werden.

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 weiterhin Aufwand, der groß sein kann, wenn sie mit der Arbeit verglichen werden, die der Job tut.

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 erheblicher Wartezeit – Ein Skript, das Zeit auf I/O- oder Remoteanrufergebnisse wartet. Die Ausführung parallel 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 erledigen. 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. Dieses Mal wird ForEach-Object sequenziell auf einem einzelnen Thread ohne Auftragsaufwand ausgeführt. Es schließt in nur 7 Millisekunden ab.

Im folgenden Beispiel werden bis zu 5000 Einträge für 10 separate Systemprotokolle gesammelt. Da das Skript eine Reihe von Protokollen liest, 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 die Threadbasierten Aufträge zu übergeben.

Start-ThreadJob kann Variablen akzeptieren, die an das Cmdlet weitergeleitet werden, an den Skriptblock über das $using Schlüsselwort ü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 verrohrte Variablen und Variablen, die direkt über das Schlüsselwort an den $using 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

Siehe auch