about_Thread_Jobs

Korte beschrijving

Bevat informatie over PowerShell-taken op basis van 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 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 de ene taak een kritieke fout heeft die het proces vastloopt, worden alle andere taken in het proces beëindigd.

Threadtaken vereisen echter minder overhead. Ze gebruiken de externe laag of serialisatie niet. 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 threadtaken 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 voor het beheren van threadtaken.

Start-ThreadJob gebruiken

De ThreadJob-module is 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. Het verzamelt de resultaten van de taak 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. 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 wordt doorgesluisd naar de opdracht. Elke onderliggende taak voert de Write-Output opdracht uit met een doorgesluisde invoerwaarde als het argument.

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

Met de ForEach-Object -Parallel opdracht wordt een PSTaskJob object geretourneerd dat onderliggende taken bevat voor elke doorgesluisde invoerwaarde. Het taakobject bevat nuttige informatie over de actieve status van onderliggende taken. Het verzamelt de resultaten van de onderliggende taken 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 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. Vervolgens wordt gewacht totdat 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, waarna wordt gewacht totdat alle onderliggende taken zijn voltooid en ten slotte alle gegevensresultaten worden geretourneerd die zijn gegenereerd door de onderliggende taken.

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

Thread-taken zijn sneller en lichter dan andere soorten 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 voldoen aan deze criteria.

  • 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 wordt meestal sneller voltooid dan als 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 waarmee 1000 threadtaken worden gemaakt om een eenvoudige tekenreeks te schrijven. Vanwege de overhead van de taak duurt het meer dan 36 seconden om te voltooien.

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 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 doorgesluisde variabelen en variabelen worden rechtstreeks via het $using trefwoord doorgegeven aan het 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 wordt doorgegeven aan de taak zorgvuldig worden behandeld. Als het geen thread-veilig object is, moet het nooit worden toegewezen aan en mogen er nooit methode en eigenschappen op worden aangeroepen.

In het volgende voorbeeld wordt een .NET-object ConcurrentDictionary met threadveilig doorgegeven aan alle onderliggende taken om unieke benoemde procesobjecten 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