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
ellerThreadJob
– 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:
- Använd
Invoke-Command
för att skapa jobb som körs i frånkopplade sessioner. Mer information finns i about_Remote_Jobs. - 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-modulenForEach-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