Delen via


about_Thread_Jobs

Korte beschrijving

Bevat informatie over taken op basis van PowerShell-threads. 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 taaktypen 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 in een afzonderlijk proces op de lokale computer uitgevoerd. Zie About Jobs (Taken) voor meer informatie.
  • PSTaskJob or ThreadJob - Opdrachten en scripts worden uitgevoerd in een afzonderlijke thread binnen hetzelfde proces op de lokale computer.

Taken op basis van threads zijn niet zo robuust als externe taken en achtergrondtaken, omdat ze in hetzelfde proces op verschillende threads worden uitgevoerd. Als één taak een kritieke fout heeft waardoor het proces vastloopt, worden alle andere taken in het proces beëindigd.

Thread-taken vereisen echter minder overhead. 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 taken op basis van threads sneller uitgevoerd en gebruiken ze minder resources dan de andere taaktypen.

Belangrijk

De bovenliggende sessie waarmee de taak is 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 de onderliggende processen beëindigd.

Er zijn twee manieren om deze situatie te omzeilen:

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

Taken op basis van threads starten en beheren

Er zijn twee manieren om taken op basis van threads te starten:

  • Start-ThreadJob- uit 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 taken op basis van threads te beheren.

Start-ThreadJob gebruiken

De ThreadJob-module is voor het eerst geleverd met PowerShell 6. Het kan ook worden geïnstalleerd vanaf 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, waaronder de huidige actieve status. De resultaten van de taak worden verzameld terwijl de resultaten worden gegenereerd.

ForEach-Object -Parallel -AsJob gebruiken

PowerShell 7.0 heeft een nieuwe parameter toegevoegd die is ingesteld op 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. De -AsJob parameter maakt taakobjecten 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 doorsnijd invoerwaarde als argument.

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

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

Wachten tot een taak is voltooid en taakresultaten ophalen

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

Met de volgende opdracht wordt een threadtaak gestart waarmee een Get-Process opdracht wordt uitgevoerd, wordt gewacht tot de opdracht is voltooid en worden ten slotte alle gegevensresultaten 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, vervolgens wacht tot alle onderliggende taken zijn voltooid en ten slotte alle gegevensresultaten retourneren 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 threadtaken

Threadtaken zijn sneller en lichter van gewicht 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. Er kan slechts één opdracht of script tegelijk in een sessie worden uitgevoerd. Dus wanneer u meerdere taken uitvoert, wordt elke taak in een afzonderlijke sessie uitgevoerd. 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 voor die aan dit criterium voldoen.

  • Werk is rekenintensief: het uitvoeren van een script op 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 gaat meestal sneller dan wanneer het achter elkaar 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 taakoverhead duurt het meer dan 36 seconden om deze te voltooien.

In het tweede voorbeeld wordt de ForEach cmdlet uitgevoerd om dezelfde 1000 bewerkingen uit te voeren. Deze keer ForEach-Object wordt sequentieel uitgevoerd, op één thread, zonder taakoverhead. Het is 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, worden doorgegeven aan het scriptblok via het $using trefwoord of worden 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 doorsluisde variabelen en variabelen die rechtstreeks via het trefwoord worden doorgegeven aan het $using scriptblok.

$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 type variabele verwijzing dat aan de taak wordt doorgegeven, zorgvuldig worden behandeld. Als het geen thread-veilig object is, moet het nooit worden toegewezen aan en mogen de methode en eigenschappen er nooit op worden aangeroepen.

In het volgende voorbeeld wordt een thread-safe .NET-object ConcurrentDictionary doorgegeven aan alle onderliggende taken om procesobjecten met een unieke naam te verzamelen. Omdat het een thread-veilig object 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