about_Thread_Jobs
Krátký popis
Poskytuje informace o úlohách založených na vláknech PowerShellu. Úloha vlákna je typ úlohy na pozadí, která spouští příkaz nebo výraz v samostatném vlákně v rámci aktuálního procesu relace.
Dlouhý popis
PowerShell souběžně spouští příkazy a skripty prostřednictvím úloh. PowerShell poskytuje tři typy úloh, které podporují souběžnost.
RemoteJob
– Příkazy a skripty se spouštějí ve vzdálené relaci. Informace najdete v tématu about_Remote_Jobs.BackgroundJob
– Příkazy a skripty se spouští v samostatném procesu na místním počítači. Další informace najdete v tématu Informace o úlohách.PSTaskJob
neboThreadJob
– Příkazy a skripty se spouští v samostatném vlákně v rámci stejného procesu na místním počítači.
Úlohy založené na vláknech nejsou tak robustní jako vzdálené úlohy a úlohy na pozadí, protože běží ve stejném procesu na různých vláknech. Pokud má jedna úloha kritickou chybu, která proces chybově ukončí, všechny ostatní úlohy v procesu se ukončí.
Úlohy založené na vláknech ale vyžadují menší režii. Nepoužívají vrstvu vzdálené komunikace ani serializaci. Výsledné objekty se vrátí jako odkazy na živé objekty v aktuální relaci. Bez této režie běží úlohy založené na vláknech rychleji a používají méně prostředků než ostatní typy úloh.
Důležité
Nadřazená relace, která vytvořila úlohu, také monitoruje stav úlohy a shromažďuje data kanálu. Podřízený proces úlohy se ukončí nadřazeným procesem, jakmile úloha dosáhne stavu dokončení. Pokud je nadřazená relace ukončena, všechny spuštěné podřízené úlohy se ukončí spolu s podřízenými procesy.
Tato situace se dá obejít dvěma způsoby:
- Slouží
Invoke-Command
k vytváření úloh spuštěných v odpojených relacích. Další informace najdete v tématu about_Remote_Jobs. - Slouží
Start-Process
k vytvoření nového procesu místo úlohy. Další informace naleznete v tématu Start-Process.
Jak spustit a spravovat úlohy založené na vláknech
Úlohy založené na vláknech můžete spustit dvěma způsoby:
Start-ThreadJob
– z modulu ThreadJobForEach-Object -Parallel -AsJob
– paralelní funkce byla přidána v PowerShellu 7.0.
Ke správě úloh založených na vláknech použijte stejné rutiny úloh úloh, které jsou popsané v about_Jobs .
Používání akce Start-ThreadJob
Modul ThreadJob byl poprvé dodán s PowerShellem 6. Dá se také nainstalovat z Galerie prostředí PowerShell pro Windows PowerShell 5.1.
Pokud chcete spustit úlohu vlákna v místním počítači, použijte Start-ThreadJob
rutinu s příkazem nebo skriptem uzavřeným ve složených závorkách ({ }
).
Následující příklad spustí úlohu vlákna, která spustí Get-Process
příkaz v místním počítači.
Start-ThreadJob -ScriptBlock { Get-Process }
Příkaz Start-ThreadJob
vrátí ThreadJob
objekt, který představuje spuštěnou úlohu. Objekt úlohy obsahuje užitečné informace o úloze, včetně jejího aktuálního stavu spuštění. Shromáždí výsledky úlohy při generování výsledků.
Používání akce ForEach-Object -Parallel -AsJob
PowerShell 7.0 přidal do ForEach-Object
rutiny novou sadu parametrů. Nové parametry umožňují spouštět bloky skriptů v paralelních vláknech jako úlohy PowerShellu.
Data můžete pipetovat do ForEach-Object -Parallel
. Data se předávají do bloku skriptu, který se spouští paralelně. Parametr -AsJob
vytvoří objekty úloh pro každé paralelní vlákna.
Následující příkaz spustí úlohu, která obsahuje podřízené úlohy pro každou vstupní hodnotu předanou do příkazu. Každá podřízená úloha spustí Write-Output
příkaz s předanou vstupní hodnotou jako argument.
1..5 | ForEach-Object -Parallel { Write-Output $_ } -AsJob
Příkaz ForEach-Object -Parallel
vrátí PSTaskJob
objekt, který obsahuje podřízené úlohy pro každou vstupní hodnotu kanálu. Objekt úlohy obsahuje užitečné informace o stavu spuštěných podřízených úloh. Shromažďuje výsledky podřízených úloh při generování výsledků.
Jak počkat na dokončení úlohy a načtení výsledků úlohy
Můžete použít rutiny úlohy PowerShellu, například Wait-Job
a Receive-Job
počkat na dokončení úlohy a pak vrátit všechny výsledky vygenerované úlohou.
Následující příkaz spustí úlohu vlákna, která spustí Get-Process
příkaz, pak počká na dokončení příkazu a nakonec vrátí všechny výsledky dat vygenerované příkazem.
Start-ThreadJob -ScriptBlock { Get-Process } | Wait-Job | Receive-Job
Následující příkaz spustí úlohu, která spustí Write-Output
příkaz pro každý vstup s kanálem, pak čeká na dokončení všech podřízených úloh a nakonec vrátí všechny výsledky dat vygenerované podřízenými úlohami.
1..5 | ForEach-Object -Parallel { Write-Output $_ } -AsJob | Wait-Job | Receive-Job
Rutina Receive-Job
vrátí výsledky podřízených úloh.
1
3
2
4
5
Vzhledem k tomu, že každá podřízená úloha běží paralelně, není zaručeno pořadí vygenerovaných výsledků.
Výkon úlohy vlákna
Úlohy vláken jsou rychlejší a lehčí než jiné typy úloh. Ale stále mají režii, která může být velká v porovnání s prací, kterou dělá úloha.
PowerShell spouští příkazy a skripty v relaci. V relaci se dá spustit jenom jeden příkaz nebo skript. Takže při spouštění více úloh se každá úloha spouští v samostatné relaci. Každá relace přispívá k režii.
Úlohy vláken poskytují nejlepší výkon při práci, kterou provádějí, vyšší než režie relace použitá ke spuštění úlohy. Existují dva případy, kdy tato kritéria splňují.
Práce je náročná na výpočetní výkon – Spuštění skriptu na více úlohách vláken může využívat více jader procesoru a dokončit je rychleji.
Práce se skládá z významného čekání – skript, který tráví čas čekáním na vstupně-výstupní operace nebo výsledky vzdáleného volání. Paralelní spouštění obvykle probíhá rychleji, než když se spouští postupně.
(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
První příklad výše ukazuje smyčku foreach, která vytvoří 1000 úloh vlákna pro jednoduché zápis řetězce. Vzhledem k režii úlohy trvá dokončení více než 36 sekund.
Druhý příklad spustí rutinu ForEach
, která provede stejné operace 1000.
ForEach-Object
Tentokrát běží postupně na jednom vlákně bez režijních nákladů na úlohy. Dokončí se v pouhých 7 milisekundách.
V následujícím příkladu se shromažďuje až 5 000 položek pro 10 samostatných systémových protokolů. Vzhledem k tomu, že skript zahrnuje čtení řady protokolů, dává smysl provádět operace paralelně.
$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
Skript se dokončí v polovině času, kdy jsou úlohy spuštěny paralelně.
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
Úlohy a proměnné vláken
Existuje několik způsobů, jak předat hodnoty do úloh založených na vláknech.
Start-ThreadJob
může přijímat proměnné, které jsou předány rutině, předány do bloku skriptu prostřednictvím klíčového $using
slova nebo předány prostřednictvím parametru 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
přijímá kanály v proměnných a proměnné předané přímo do bloku skriptu prostřednictvím klíčového $using
slova.
$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
Vzhledem k tomu, že úlohy vlákna běží ve stejném procesu, je nutné pečlivě zpracovat jakýkoli typ odkazu proměnné předaný do úlohy. Pokud se nejedná o bezpečný objekt vlákna, neměl by být nikdy přiřazen a metoda a vlastnosti by se na něm nikdy neměly vyvolat.
Následující příklad předá objekt .NET ConcurrentDictionary
bezpečný pro přístup z více vláken do všech podřízených úloh, aby se shromažďoval jedinečně pojmenované objekty procesu. Vzhledem k tomu, že se jedná o bezpečný objekt vlákna, lze ho bezpečně použít, zatímco úlohy běží souběžně v procesu.
$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