about_Thread_Jobs

Korte beschrijving

Bevat informatie over op PowerShell thread gebaseerde taken. Een threadtaak is een type achtergrondtaak waarmee een opdracht of expressie wordt uitgevoerd in een afzonderlijke thread binnen het huidige sessieproces.

Lange beschrijving

PowerShell voert gelijktijdig opdrachten en scripts uit via taken. PowerShell biedt drie typen taken ter ondersteuning van gelijktijdigheid.

  • RemoteJob - Opdrachten en scripts worden uitgevoerd in een externe sessie. Zie about_Remote_Jobs voor meer informatie.
  • BackgroundJob - Opdrachten en scripts worden uitgevoerd in een afzonderlijk proces op de lokale computer. Zie About Jobs (Taken) voor meer informatie.
  • PSTaskJob of ThreadJob - Opdrachten en scripts worden uitgevoerd in een afzonderlijke thread binnen hetzelfde proces op de lokale computer.

Thread-taken zijn niet zo robuust als externe en achtergrondtaken, omdat ze in hetzelfde proces op verschillende threads worden uitgevoerd. Als een taak een kritieke fout heeft die het proces vastloopt, worden alle andere taken in het proces beëindigd.

Voor threadgebaseerde taken is echter minder overhead vereist. Ze maken geen gebruik van de externe laag of serialisatie. De resultaatobjecten worden geretourneerd als verwijzingen naar live-objecten in de huidige sessie. Zonder deze overhead worden threadtaken sneller uitgevoerd en worden er minder resources gebruikt dan de andere taaktypen.

Belangrijk

De bovenliggende sessie die de taak heeft gemaakt, bewaakt ook de taakstatus en verzamelt pijplijngegevens. Het onderliggende taakproces wordt beëindigd door het bovenliggende proces zodra de taak een voltooide status heeft bereikt. Als de bovenliggende sessie wordt beëindigd, worden alle actieve onderliggende taken samen met hun onderliggende processen beëindigd.

Er zijn twee manieren om deze situatie te omzeilen:

  1. Gebruik Invoke-Command dit om taken te maken die worden uitgevoerd in niet-verbonden sessies. Zie about_Remote_Jobs voor meer informatie.
  2. Gebruik Start-Process dit om een nieuw proces te maken in plaats van een taak. Zie Start-Process voor meer informatie.

Op threads gebaseerde taken starten en beheren

Er zijn twee manieren om op threads gebaseerde taken te starten:

  • Start-ThreadJob- van de ThreadJob-module
  • ForEach-Object -Parallel -AsJob - de parallelle functie is toegevoegd in PowerShell 7.0

Gebruik dezelfde taak-cmdlets die worden beschreven in about_Jobs om op threads gebaseerde taken te beheren.

Start-ThreadJob gebruiken

De ThreadJob-module is voor het eerst geleverd met PowerShell 6. Het kan ook worden geïnstalleerd vanuit de PowerShell Gallery voor Windows PowerShell 5.1.

Als u een threadtaak op de lokale computer wilt starten, gebruikt u de Start-ThreadJob cmdlet met een opdracht of script tussen accolades ({ }).

In het volgende voorbeeld wordt een threadtaak gestart waarmee een Get-Process opdracht op de lokale computer wordt uitgevoerd.

Start-ThreadJob -ScriptBlock { Get-Process }

De Start-ThreadJob opdracht retourneert een ThreadJob object dat de actieve taak vertegenwoordigt. Het taakobject bevat nuttige informatie over de taak, inclusief de huidige actieve status. Hiermee worden de resultaten van de taak verzameld wanneer de resultaten worden gegenereerd.

ForEach-Object -Parallel -AsJob gebruiken

PowerShell 7.0 heeft een nieuwe parameterset toegevoegd aan de ForEach-Object cmdlet. Met de nieuwe parameters kunt u scriptblokken in parallelle threads uitvoeren als PowerShell-taken.

U kunt gegevens doorsluisen naar ForEach-Object -Parallel. De gegevens worden doorgegeven aan het scriptblok dat parallel wordt uitgevoerd. Met -AsJob de parameter worden taakobjecten gemaakt voor elk van de parallelle threads.

Met de volgende opdracht wordt een taak gestart die onderliggende taken bevat voor elke invoerwaarde die naar de opdracht wordt doorgesluisd. Elke onderliggende taak voert de Write-Output opdracht uit met een invoerwaarde met piped als het argument.

1..5 | ForEach-Object -Parallel { Write-Output $_ } -AsJob

De ForEach-Object -Parallel opdracht retourneert een PSTaskJob object dat onderliggende taken bevat voor elke invoerwaarde met pipeds. Het taakobject bevat nuttige informatie over de status van de onderliggende taken die worden uitgevoerd. Het verzamelt de resultaten van de onderliggende taken wanneer de resultaten worden gegenereerd.

Wachten totdat een taak is voltooid en taakresultaten ophalen

U kunt PowerShell-taak-cmdlets gebruiken, zoals Wait-Job en Receive-Job wachten tot een taak is voltooid en vervolgens alle resultaten retourneren die door de taak zijn gegenereerd.

Met de volgende opdracht wordt een threadtaak gestart waarmee een Get-Process opdracht wordt uitgevoerd, waarna wordt gewacht totdat de opdracht is voltooid en ten slotte alle gegevensresultaten worden geretourneerd die door de opdracht zijn gegenereerd.

Start-ThreadJob -ScriptBlock { Get-Process } | Wait-Job | Receive-Job

Met de volgende opdracht wordt een taak gestart die een Write-Output opdracht uitvoert voor elke doorgesluisde invoer, waarna wordt gewacht tot alle onderliggende taken zijn voltooid en ten slotte alle gegevensresultaten worden geretourneerd die door de onderliggende taken zijn gegenereerd.

1..5 | ForEach-Object -Parallel { Write-Output $_ } -AsJob | Wait-Job | Receive-Job

De Receive-Job cmdlet retourneert de resultaten van de onderliggende taken.

1
3
2
4
5

Omdat elke onderliggende taak parallel wordt uitgevoerd, wordt de volgorde van de gegenereerde resultaten niet gegarandeerd.

Prestaties van threadtaak

Threadtaken zijn sneller en lichter dan andere typen taken. Maar ze hebben nog steeds overhead die groot kan zijn in vergelijking met het werk dat de taak doet.

PowerShell voert opdrachten en scripts uit in een sessie. Slechts één opdracht of script kan tegelijk in een sessie worden uitgevoerd. Dus bij het uitvoeren van meerdere taken wordt elke taak uitgevoerd in een afzonderlijke sessie. Elke sessie draagt bij aan de overhead.

Threadtaken bieden de beste prestaties wanneer het werk dat ze uitvoeren groter is dan de overhead van de sessie die wordt gebruikt om de taak uit te voeren. Er zijn twee gevallen die aan deze criteria voldoen.

  • Werk is rekenintensief: het uitvoeren van een script voor meerdere threadtaken kan profiteren van meerdere processorkernen en sneller voltooien.

  • Werk bestaat uit aanzienlijke wachttijden: een script dat tijd besteedt aan het wachten op I/O- of externe oproepresultaten. Parallel uitvoeren verloopt meestal sneller dan als de uitvoering sequentieel wordt uitgevoerd.

(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

In het eerste voorbeeld hierboven ziet u een foreach-lus waarmee 1000 threadtaken worden gemaakt om een eenvoudige tekenreeks te schrijven. Vanwege de overhead van de taak duurt het meer dan 36 seconden.

In het tweede voorbeeld wordt de ForEach cmdlet uitgevoerd om dezelfde 1000 bewerkingen uit te voeren. Deze keer wordt ForEach-Object deze keer opeenvolgend uitgevoerd, op één thread, zonder taakoverhead. Het wordt in slechts 7 milliseconden voltooid.

In het volgende voorbeeld worden maximaal 5000 vermeldingen verzameld voor 10 afzonderlijke systeemlogboeken. Omdat het script betrekking heeft op het lezen van een aantal logboeken, is het zinvol om de bewerkingen parallel uit te voeren.

$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

Het script wordt in de helft van de tijd voltooid wanneer de taken parallel worden uitgevoerd.

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

Threadtaken en -variabelen

Er zijn meerdere manieren om waarden door te geven aan de thread-taken.

Start-ThreadJob kan variabelen accepteren die worden doorgesluisd naar de cmdlet, doorgegeven aan het scriptblok via het $using trefwoord of doorgegeven via de parameter ArgumentList .

$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 accepteert pijpen in variabelen en variabelen die rechtstreeks via het $using trefwoord aan het scriptblok worden doorgegeven.

$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

Aangezien threadtaken in hetzelfde proces worden uitgevoerd, moet elk variabele referentietype dat in de taak wordt doorgegeven zorgvuldig worden behandeld. Als het geen thread-veilig object is, mag het nooit worden toegewezen aan en mogen er nooit methoden en eigenschappen op worden aangeroepen.

In het volgende voorbeeld wordt een threadveilig .NET-object ConcurrentDictionary doorgegeven aan alle onderliggende taken om unieke procesobjecten met een unieke naam te verzamelen. Omdat het een veilig threadobject is, kan het veilig worden gebruikt terwijl de taken gelijktijdig in het proces worden uitgevoerd.

$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

Zie ook