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 współbieżnie uruchamia polecenia i skrypty za pośrednictwem zadań. Istnieją trzy typy zadań udostępniane przez program PowerShell 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 sieciowej 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ą mniejszej liczby 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 procesami podrzędnymi.

Istnieją dwa sposoby obejścia tej sytuacji:

  1. Służy Invoke-Command do tworzenia zadań uruchamianych w sesjach bez połączenia. 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 — funkcja równoległa została dodana w programie PowerShell 7.0

Użyj tych samych poleceń cmdlet zadań opisanych w about_Jobs , aby zarządzać 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 programu 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 na ForEach-Object polecenie cmdlet. Nowe parametry umożliwiają uruchamianie bloków skryptów w wątkach równoległych jako zadań programu PowerShell.

Możesz przekazać dane potokowe 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, które zawiera zadania podrzędne dla każdej wartości wejściowej przesyłane 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 zawierający zadania podrzędne dla każdej wartości wejściowej potoku. Obiekt zadania zawiera przydatne informacje o stanie uruchamiania zadań podrzędnych. Zbiera wyniki zadań podrzędnych w miarę 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 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 danych wejściowych, 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 zadań podrzędnych.

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 lżejsze niż inne typy zadań. Ale nadal mają nakłady pracy, które mogą być duże w porównaniu z pracą, którą wykonuje praca.

Program PowerShell uruchamia polecenia i skrypty w sesji. W sesji można uruchomić tylko jedno polecenie lub skrypt. Dlatego podczas 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 praca, którą wykonują, jest większa niż obciążenie sesji użytej do uruchomienia zadania. Istnieją dwa przypadki, które spełniają te kryteria.

  • Praca jest intensywnie obciążana obliczeniami — uruchamianie skryptu w wielu zadaniach wątków może korzystać z wielu rdzeni procesora i wykonywać je szybciej.

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

(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ątku w celu wykonania prostego zapisu ciągów. Ze względu na obciążenie zadania ukończenie zajmuje 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 do 5000 wpisów jest zbieranych 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 może akceptować zmienne przesyłane potokiem do polecenia cmdlet, przekazywane do bloku skryptu za pośrednictwem słowa kluczowego lub przekazywane za $using pośrednictwem 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 zmiennej przekazany do zadania musi być dokładnie traktowany. Jeśli nie jest to obiekt bezpieczny wątku, nigdy nie należy go przypisywać, a metody i właściwości 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 procesów. 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ż