about_Thread_Jobs

Krótki opis

Zawiera informacje o zadaniach opartych na wątkach programu PowerShell. Zadanie wątku jest typem zadania w tle, które uruchamia polecenie lub wyrażenie w osobnym wątku w ramach bieżącego procesu sesji.

Długi opis

Program PowerShell jednocześnie uruchamia polecenia i skrypty za pośrednictwem zadań. Program PowerShell udostępnia trzy typy zadań do obsługi współbieżności.

  • RemoteJob - Polecenia i skrypty są uruchamiane w sesji zdalnej. Aby uzyskać informacje, zobacz about_Remote_Jobs.
  • BackgroundJob - Polecenia i skrypty są uruchamiane w osobnym procesie na komputerze lokalnym. Aby uzyskać więcej informacji, zobacz opis polecenia about_Jobs.
  • PSTaskJob lub ThreadJob — polecenia i skrypty są uruchamiane w osobnym wątku w ramach tego samego procesu na komputerze lokalnym.

Zadania oparte na wątkach nie są tak niezawodne jak zadania zdalne i w tle, ponieważ są uruchamiane w tym samym procesie w różnych wątkach. Jeśli jedno zadanie ma krytyczny błąd powodujący awarię procesu, wszystkie inne zadania w procesie zostaną zakończone.

Jednak zadania oparte na wątkach wymagają mniejszego obciążenia. Nie używają warstwy komunikacji zdalnej ani serializacji. Obiekty wynikowe są zwracane jako odwołania do obiektów na żywo w bieżącej sesji. Bez tego obciążenia zadania oparte na wątkach działają szybciej i używają mniej zasobów niż inne typy zadań.

Ważne

Sesja nadrzędna, która utworzyła zadanie, monitoruje również stan zadania i zbiera dane potoku. Proces podrzędny zadania zostaje zakończony przez proces nadrzędny po osiągnięciu stanu zakończenia zadania. Jeśli sesja nadrzędna zostanie zakończona, wszystkie uruchomione zadania podrzędne zostaną zakończone wraz z ich procesami podrzędnymi.

Istnieją dwa sposoby obejścia tej sytuacji:

  1. Służy Invoke-Command do tworzenia zadań uruchamianych w rozłączonych sesjach. Aby uzyskać więcej informacji, zobacz about_Remote_Jobs.
  2. Użyj Start-Process polecenia , aby utworzyć nowy proces, a nie zadanie. Aby uzyskać więcej informacji, zobacz Rozpoczynanie procesu.

Jak uruchamiać zadania oparte na wątkach i zarządzać nimi

Istnieją dwa sposoby uruchamiania zadań opartych na wątkach:

  • Start-ThreadJob— z modułu ThreadJob
  • ForEach-Object -Parallel -AsJob — dodano funkcję równoległą w programie PowerShell 7.0

Użyj tych samych poleceń cmdlet zadań opisanych w about_Jobs do zarządzania zadaniami opartymi na wątkach.

Korzystanie z akcji Start-ThreadJob

Moduł ThreadJob został po raz pierwszy dostarczony z programem PowerShell 6. Można go również zainstalować z Galeria programu PowerShell dla Windows PowerShell 5.1.

Aby uruchomić zadanie wątku na komputerze lokalnym, użyj Start-ThreadJob polecenia cmdlet z poleceniem lub skryptem ujętym w nawiasy klamrowe ({ }).

Poniższy przykład uruchamia zadanie wątku, które uruchamia Get-Process polecenie na komputerze lokalnym.

Start-ThreadJob -ScriptBlock { Get-Process }

Polecenie Start-ThreadJob zwraca ThreadJob obiekt reprezentujący uruchomione zadanie. Obiekt zadania zawiera przydatne informacje o zadaniu, w tym jego bieżący stan uruchomienia. Zbiera wyniki zadania w miarę generowania wyników.

Korzystanie z akcji ForEach-Object -Parallel -AsJob

Program PowerShell 7.0 dodał nowy parametr ustawiony do ForEach-Object polecenia cmdlet. Nowe parametry umożliwiają uruchamianie bloków skryptu w równoległych wątkach jako zadań programu PowerShell.

Dane można przekazać potokowi do .ForEach-Object -Parallel Dane są przekazywane do bloku skryptu, który jest uruchamiany równolegle. Parametr -AsJob tworzy obiekty zadań dla każdego wątku równoległego.

Następujące polecenie uruchamia zadanie zawierające zadania podrzędne dla każdej wartości wejściowej przesyłanej potokiem do polecenia. Każde zadanie podrzędne uruchamia Write-Output polecenie z potokową wartością wejściową jako argumentem.

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

Polecenie ForEach-Object -Parallel zwraca PSTaskJob obiekt, który zawiera podrzędne zadania dla każdej wartości wejściowej potoku. Obiekt zadania zawiera przydatne informacje o stanie uruchamiania zadań podrzędnych. Zbiera wyniki podrzędnych zadań podczas generowania wyników.

Jak poczekać na ukończenie zadania i pobrać wyniki zadania

Możesz użyć poleceń cmdlet zadań programu PowerShell, takich jak Wait-Job i Receive-Job , aby poczekać na ukończenie zadania, a następnie zwrócić wszystkie wyniki wygenerowane przez zadanie.

Następujące polecenie uruchamia zadanie wątku, które uruchamia Get-Process polecenie, a następnie czeka na ukończenie polecenia, a na koniec zwraca wszystkie wyniki danych wygenerowane przez polecenie .

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

Następujące polecenie uruchamia zadanie, które uruchamia Write-Output polecenie dla każdego potokowego wejścia, a następnie czeka na ukończenie wszystkich zadań podrzędnych, a na koniec zwraca wszystkie wyniki danych wygenerowane przez zadania podrzędne.

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

Polecenie Receive-Job cmdlet zwraca wyniki podrzędnych zadań.

1
3
2
4
5

Ponieważ każde zadanie podrzędne jest uruchamiane równolegle, kolejność wygenerowanych wyników nie jest gwarantowana.

Wydajność zadania wątku

Zadania wątków są szybsze i mniejsze niż inne typy zadań. Ale nadal mają narzut, który może być duży w porównaniu do pracy, którą wykonuje praca.

Program PowerShell uruchamia polecenia i skrypty w sesji. W sesji można uruchomić tylko jedno polecenie lub skrypt. Dlatego w przypadku uruchamiania wielu zadań każde zadanie jest uruchamiane w oddzielnej sesji. Każda sesja przyczynia się do narzutów.

Zadania wątków zapewniają najlepszą wydajność, gdy wykonywana praca jest większa niż obciążenie sesji użytej do uruchomienia zadania. Istnieją dwa przypadki, w których spełniają te kryteria.

  • Praca wymaga dużej mocy obliczeniowej — uruchamianie skryptu w wielu zadaniach wątków może korzystać z wielu rdzeni procesora i szybciej wykonywać zadania.

  • Praca składa się ze znaczącego oczekiwania — skrypt, który spędza czas na oczekiwaniu na wyniki we/wy lub wywołania zdalnego. Uruchamianie równoległe zwykle odbywa się szybciej niż w przypadku sekwencyjnego uruchamiania.

(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

W pierwszym przykładzie powyżej przedstawiono pętlę foreach, która tworzy 1000 zadań wątków w celu wykonania prostego zapisu ciągów. Ze względu na obciążenie zadania ukończenie trwa ponad 36 sekund.

Drugi przykład uruchamia polecenie cmdlet, ForEach aby wykonać te same 1000 operacji. Tym razem ForEach-Object uruchamiane sekwencyjnie, w jednym wątku, bez żadnych obciążeń związanych z zadaniem. Kończy się w zaledwie 7 milisekundach.

W poniższym przykładzie zebrano maksymalnie 5000 wpisów dla 10 oddzielnych dzienników systemowych. Ponieważ skrypt obejmuje odczytywanie wielu dzienników, warto wykonać operacje równolegle.

$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

Skrypt kończy się w połowie czasu, gdy zadania są uruchamiane równolegle.

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

Zadania wątku i zmienne

Istnieje wiele sposobów przekazywania wartości do zadań opartych na wątkach.

Start-ThreadJob Program może akceptować zmienne przesyłane potokiem do polecenia cmdlet, przekazywane do bloku skryptów za pośrednictwem słowa kluczowego lub przekazywane za pośrednictwem $using 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 akceptuje potok w zmiennych, a zmienne przekazywane bezpośrednio do bloku skryptu za pośrednictwem słowa kluczowego $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

Ponieważ zadania wątku są uruchamiane w tym samym procesie, każdy typ odwołania do zmiennej przekazany do zadania musi być dokładnie traktowany. Jeśli nie jest to bezpieczny wątek obiekt, nigdy nie należy go przypisywać, a właściwości i metody nigdy nie powinny być wywoływane na nim.

Poniższy przykład przekazuje obiekt .NET ConcurrentDictionary bezpieczny wątkowo do wszystkich zadań podrzędnych w celu zbierania unikatowych nazwanych obiektów procesu. Ponieważ jest to bezpieczny wątek obiekt, można go bezpiecznie używać, gdy zadania są uruchamiane współbieżnie w procesie.

$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

Zobacz też