about_Thread_Jobs

Kort beskrivning

Innehåller information om PowerShell-trådbaserade jobb. Ett trådjobb är en typ av bakgrundsjobb som kör ett kommando eller uttryck i en separat tråd i den aktuella sessionsprocessen.

Lång beskrivning

PowerShell kör samtidigt kommandon och skript via jobb. Det finns tre jobbtyper som tillhandahålls av PowerShell för att stödja samtidighet.

  • RemoteJob – Kommandon och skript körs i en fjärrsession. Mer information finns i about_Remote_Jobs.
  • BackgroundJob – Kommandon och skript körs i en separat process på den lokala datorn. Mer information finns i artikeln om jobb.
  • PSTaskJob eller ThreadJob – Kommandon och skript körs i en separat tråd i samma process på den lokala datorn.

Trådbaserade jobb är inte lika robusta som fjärr- och bakgrundsjobb, eftersom de körs i samma process på olika trådar. Om ett jobb har ett kritiskt fel som kraschar processen avslutas alla andra jobb i processen.

Trådbaserade jobb kräver dock mindre omkostnader. De använder inte fjärrkommunikationsskiktet eller serialiseringen. Resultatobjekten returneras som referenser till levande objekt i den aktuella sessionen. Utan den här kostnaden körs trådbaserade jobb snabbare och använder färre resurser än de andra jobbtyperna.

Viktigt!

Den överordnade sessionen som skapade jobbet övervakar också jobbstatusen och samlar in pipelinedata. Den underordnade jobbprocessen avslutas av den överordnade processen när jobbet når ett slutfört tillstånd. Om den överordnade sessionen avslutas avslutas alla underordnade jobb som körs tillsammans med deras underordnade processer.

Det finns två sätt att kringgå den här situationen:

  1. Använd Invoke-Command för att skapa jobb som körs i frånkopplade sessioner. Mer information finns i about_Remote_Jobs.
  2. Använd Start-Process för att skapa en ny process i stället för ett jobb. Mer information finns i Startprocess.

Så här startar och hanterar du trådbaserade jobb

Det finns två sätt att starta trådbaserade jobb:

  • Start-ThreadJob– från ThreadJob-modulen
  • ForEach-Object -Parallel -AsJob – den parallella funktionen lades till i PowerShell 7.0

Använd samma jobb-cmdletar som beskrivs i about_Jobs för att hantera trådbaserade jobb.

Använda Start-ThreadJob

ThreadJob-modulen levererades först med PowerShell 6. Det kan också installeras från PowerShell-galleriet för Windows PowerShell 5.1.

Om du vill starta ett trådjobb på den lokala datorn använder du cmdleten Start-ThreadJob med ett kommando eller skript omgivet av klammerparenteser ({ }).

I följande exempel startas ett trådjobb som kör ett Get-Process kommando på den lokala datorn.

Start-ThreadJob -ScriptBlock { Get-Process }

Kommandot Start-ThreadJob returnerar ett ThreadJob objekt som representerar det jobb som körs. Jobbobjektet innehåller användbar information om jobbet, inklusive dess aktuella körningsstatus. Den samlar in resultatet av jobbet när resultatet genereras.

Använda ForEach-Object -Parallel -AsJob

PowerShell 7.0 har lagt till en ny parameteruppsättning i cmdleten ForEach-Object . Med de nya parametrarna kan du köra skriptblock i parallella trådar som PowerShell-jobb.

Du kan skicka data till ForEach-Object -Parallel. Data skickas till skriptblocket som körs parallellt. Parametern -AsJob skapar jobbobjekt för var och en av de parallella trådarna.

Följande kommando startar ett jobb som innehåller underordnade jobb för varje indatavärde som skickas till kommandot. Varje underordnat Write-Output jobb kör kommandot med ett piped-indatavärde som argument.

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

Kommandot ForEach-Object -Parallel returnerar ett PSTaskJob objekt som innehåller underordnade jobb för varje piped-indatavärde. Jobbobjektet innehåller användbar information om de underordnade jobb som kör status. Den samlar in resultatet av de underordnade jobben när resultatet genereras.

Vänta tills ett jobb har slutförts och hämta jobbresultat

Du kan använda PowerShell-jobb-cmdletar, till exempel Wait-Job och Receive-Job för att vänta tills ett jobb har slutförts och sedan returnera alla resultat som genererats av jobbet.

Följande kommando startar ett trådjobb som kör ett Get-Process kommando och väntar sedan på att kommandot ska slutföras och returnerar slutligen alla dataresultat som genereras av kommandot.

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

Följande kommando startar ett jobb som kör ett Write-Output kommando för varje piped-indata och väntar sedan på att alla underordnade jobb ska slutföras och returnerar slutligen alla dataresultat som genereras av de underordnade jobben.

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

Cmdleten Receive-Job returnerar resultatet av de underordnade jobben.

1
3
2
4
5

Eftersom varje underordnat jobb körs parallellt garanteras inte ordningen på de genererade resultaten.

Trådjobbsprestanda

Trådjobb är snabbare och lättare än andra typer av jobb. Men de har fortfarande omkostnader som kan vara stora jämfört med det arbete som jobbet utför.

PowerShell kör kommandon och skript i en session. Endast ett kommando eller skript kan köras i taget i en session. Så när du kör flera jobb körs varje jobb i en separat session. Varje session bidrar till omkostnaderna.

Trådjobb ger bästa möjliga prestanda när det arbete de utför är större än omkostnaderna för den session som användes för att köra jobbet. Det finns två fall för som uppfyller dessa kriterier.

  • Arbetet är beräkningsintensivt – Om du kör ett skript på flera trådjobb kan du dra nytta av flera processorkärnor och slutföras snabbare.

  • Arbetet består av betydande väntan – ett skript som ägnar tid åt att vänta på I/O- eller fjärrsamtalsresultat. Körning parallellt slutförs vanligtvis snabbare än om den körs sekventiellt.

(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

Det första exemplet ovan visar en foreach-loop som skapar 1 000 trådjobb för att göra en enkel strängskrivning. På grund av jobbkostnader tar det över 36 sekunder att slutföra.

Det andra exemplet kör cmdleten ForEach för att utföra samma 1 000 åtgärder. Den här gången ForEach-Object körs sekventiellt, på en enda tråd, utan jobbomkostnader. Den slutförs på bara 7 millisekunder.

I följande exempel samlas upp till 5 000 poster in för 10 separata systemloggar. Eftersom skriptet omfattar läsning av ett antal loggar är det klokt att utföra åtgärderna parallellt.

$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

Skriptet slutförs på halva tiden när jobben körs parallellt.

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

Trådjobb och variabler

Det finns flera sätt att skicka värden till trådbaserade jobb.

Start-ThreadJob kan acceptera variabler som skickas till cmdleten, skickas till skriptblocket via nyckelordet $using eller skickas via parametern 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 accepterar piped i variabler och variabler som skickas direkt till skriptblocket via nyckelordet $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

Eftersom trådjobb körs i samma process måste alla variabelreferenstyper som skickas till jobbet behandlas noggrant. Om det inte är ett trådsäkert objekt bör det aldrig tilldelas till, och metoden och egenskaperna ska aldrig anropas på det.

I följande exempel skickas ett trådsäkert .NET-objekt ConcurrentDictionary till alla underordnade jobb för att samla in unikt namngivna processobjekt. Eftersom det är ett trådsäkert objekt kan det användas på ett säkert sätt medan jobben körs samtidigt i processen.

$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

Se även