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 nebo ThreadJob – 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:

  1. 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.
  2. 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 ThreadJob
  • ForEach-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

Viz také