about_Thread_Jobs

Korte beschrijving

Bevat informatie over op PowerShell-thread gebaseerde taken. Een thread-taak is een type achtergrond job die een opdracht of expressie in een afzonderlijke thread binnen het huidige sessieproces wordt uitgevoerd.

Lange beschrijving

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

  • RemoteJob - Opdrachten en scripts worden uitgevoerd in een externe sessie. Zie voor meer informatie about_Remote_Jobs.
  • BackgroundJob - Opdrachten en scripts worden in een afzonderlijk proces op de lokale computer uitgevoerd. 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.

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

Op thread gebaseerde taken vereisen echter minder overhead. Ze maken geen gebruik van de remoting-laag of serialisatie. De resultaatobjecten worden geretourneerd als verwijzingen naar live-objecten in de huidige sessie. Zonder deze overhead worden threadtaken sneller uitgevoerd en gebruiken ze minder resources dan de andere taaktypen.

Belangrijk

De bovenliggende sessie die de taak heeft gemaakt, controleert ook de taakstatus en verzamelt pijplijngegevens. Het onderliggende proces van de taak wordt beëindigd door het bovenliggende proces zodra de taak de status Voltooid heeft. Als de bovenliggende sessie wordt beëindigd, worden alle onderliggende taken die worden uitgevoerd, samen met hun onderliggende processen beëindigd.

Er zijn twee manieren om deze situatie te voorkomen:

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

Threadtaken starten en beheren

Er zijn twee manieren om threadgebaseerde taken 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 voor het beheren van threadtaken.

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 thread-taak op de lokale computer wilt starten, Start-ThreadJob gebruikt u de cmdlet met een opdracht of script tussen accolades ({ }).

In het volgende voorbeeld wordt een thread-taak gestart die een opdracht Get-Process op de lokale computer wordt uitgevoerd.

Start-ThreadJob -ScriptBlock { Get-Process }

De Start-ThreadJob opdracht retourneert een ThreadJob -object dat de lopende taak vertegenwoordigt. Het taakobject bevat nuttige informatie over de taak, waaronder de huidige status van de taak. De resultaten van de taak worden verzameld terwijl 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 doorspijpen naar ForEach-Object -Parallel. De gegevens worden doorgegeven aan het scriptblok dat parallel wordt uitgevoerd. De -AsJob parameter maakt takenobjecten voor elk van de parallelle threads.

Met de volgende opdracht wordt een taak gestart die onderliggende taken bevat voor elke invoerwaarde die wordt doorspijpt naar de opdracht . Elke onderliggende taak voert de opdracht Write-Output uit met een doorsporingsinvoerwaarde 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 doorspijpinvoerwaarde. 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, zoals en, Wait-Job Receive-Job gebruiken om te wachten tot een taak is voltooid en vervolgens alle resultaten te retourneren die door de taak zijn gegenereerd.

Met de volgende opdracht start u een thread-taak Get-Process die een opdracht wordt uitgevoerd, wacht tot de opdracht is voltooid en retourneert tot slot alle gegevensresultaten die zijn gegenereerd door de opdracht.

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

Met de volgende Write-Output opdracht wordt een taak gestart die een opdracht voor elke doorspijpinvoer wordt uitgevoerd, wacht tot alle onderliggende taken zijn voltooid en ten slotte alle gegevensresultaten retourneert 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 thread-taak

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. Bij het uitvoeren van meerdere taken wordt elke taak dus 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 deze criteria voldoen.

  • Werk is rekenintensief: het uitvoeren van een script op meerdere threadtaken kan profiteren van meerdere processorkernen en sneller worden voltooid.

  • Het werk bestaat uit aanzienlijke wachttijden: een script dat tijd besteedt aan het wachten op I/O- of externe oproepresultaten. Parallel uitvoeren wordt meestal sneller voltooid dan wanneer deze opeenvolgend 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 die 1000 threadtaken maakt om een eenvoudige tekenreeks te schrijven. Vanwege de overhead van de taak duurt het langer dan 36 seconden.

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

In het volgende voorbeeld worden maximaal 5000 vermeldingen verzameld voor 10 afzonderlijke systeemlogboeken. Omdat het script een aantal logboeken moet lezen, 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 threadtaken.

Start-ThreadJob kan variabelen accepteren die worden doorgegeven aan de cmdlet, worden doorgegeven aan het scriptblok via $using het 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 doors piped in variabelen en variabelen die rechtstreeks aan het scriptblok worden doorgegeven via het trefwoord $using .

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

In het volgende voorbeeld wordt een thread-veilig .NET-object ConcurrentDictionary door geven aan alle onderliggende taken voor het verzamelen van unieke benoemde procesobjecten. 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