about_Thread_Jobs

Breve descrição

Fornece informações sobre trabalhos baseados em threads do PowerShell. Um trabalho de thread é um tipo de trabalho em segundo plano que executa um comando ou expressão em um thread separado dentro do processo de sessão atual.

Descrição longa

O PowerShell executa comandos e scripts simultaneamente por meio de trabalhos. Há três tipos de trabalhos fornecidos pelo PowerShell para dar suporte à simultaneidade.

  • RemoteJob - Comandos e scripts são executados em uma sessão remota. Para obter informações, consulte about_Remote_Jobs.
  • BackgroundJob - Comandos e scripts são executados em um processo separado na máquina local. Para obter mais informações, veja about_Jobs.
  • PSTaskJob ou ThreadJob - Comandos e scripts são executados em um thread separado dentro do mesmo processo na máquina local.

Os trabalhos baseados em threads não são tão robustos quanto os remotos e em segundo plano, porque são executados no mesmo processo em threads diferentes. Se um trabalho tiver um erro crítico que trave o processo, todos os outros trabalhos no processo serão encerrados.

No entanto, trabalhos baseados em threads exigem menos sobrecarga. Eles não usam a camada de comunicação remota ou serialização. Os objetos de resultado são retornados como referências a objetos dinâmicos na sessão atual. Sem essa sobrecarga, os trabalhos baseados em threads são executados mais rapidamente e usam menos recursos do que os outros tipos de trabalho.

Importante

A sessão pai que criou o trabalho também monitora o status do trabalho e coleta dados de pipeline. O processo filho do trabalho é encerrado pelo processo pai assim que o trabalho atinge um estado concluído. Se a sessão pai for encerrada, todos os trabalhos filho em execução serão encerrados juntamente com seus processos filho.

Há duas maneiras de contornar essa situação:

  1. Use Invoke-Command para criar trabalhos que são executados em sessões desconectadas. Para obter mais informações, consulte about_Remote_Jobs.
  2. Use Start-Process para criar um novo processo em vez de um trabalho. Para obter mais informações, consulte Start-Process.

Como iniciar e gerenciar trabalhos baseados em threads

Há duas maneiras de iniciar trabalhos baseados em threads:

  • Start-ThreadJob- do módulo ThreadJob
  • ForEach-Object -Parallel -AsJob - o recurso paralelo foi adicionado no PowerShell 7.0

Use os mesmos cmdlets Job descritos em about_Jobs para gerenciar trabalhos baseados em threads.

Ao utilizar Start-ThreadJob

O módulo ThreadJob foi fornecido pela primeira vez com o PowerShell 6. Ele também pode ser instalado a partir da Galeria do PowerShell para Windows PowerShell 5.1.

Para iniciar um trabalho de thread no computador local, use o Start-ThreadJob cmdlet com um comando ou script entre chaves ({ }).

O exemplo a seguir inicia um trabalho de thread que executa um Get-Process comando no computador local.

Start-ThreadJob -ScriptBlock { Get-Process }

O Start-ThreadJob comando retorna um ThreadJob objeto que representa o trabalho em execução. O objeto de trabalho contém informações úteis sobre o trabalho, incluindo seu status de execução atual. Ele coleta os resultados do trabalho à medida que os resultados estão sendo gerados.

Ao utilizar ForEach-Object -Parallel -AsJob

O PowerShell 7.0 adicionou um novo conjunto de parâmetros ao ForEach-Object cmdlet. Os novos parâmetros permitem executar blocos de script em threads paralelos como trabalhos do PowerShell.

Você pode canalizar dados para ForEach-Object -Parallel. Os dados são passados para o bloco de script que é executado em paralelo. O -AsJob parâmetro cria objetos jobs para cada um dos threads paralelos.

O comando a seguir inicia um trabalho que contém trabalhos filho para cada valor de entrada canalizado para o comando. Cada trabalho filho executa o Write-Output comando com um valor de entrada canalizado como argumento.

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

O ForEach-Object -Parallel comando retorna um PSTaskJob objeto que contém trabalhos filho para cada valor de entrada canalizado. O objeto de trabalho contém informações úteis sobre o status de execução dos trabalhos filho. Recolhe os resultados dos trabalhos infantis à medida que os resultados vão sendo gerados.

Como aguardar a conclusão de um trabalho e recuperar os resultados do trabalho

Você pode usar cmdlets de trabalho do PowerShell, como Wait-Job e Receive-Job para aguardar a conclusão de um trabalho e, em seguida, retornar todos os resultados gerados pelo trabalho.

O comando a seguir inicia um trabalho de thread que executa um Get-Process comando, aguarda a conclusão do comando e, finalmente, retorna todos os resultados de dados gerados pelo comando.

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

O comando a seguir inicia um trabalho que executa um Write-Output comando para cada entrada canalizada, aguarda a conclusão de todos os trabalhos filho e, finalmente, retorna todos os resultados de dados gerados pelos trabalhos filho.

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

O Receive-Job cmdlet retorna os resultados dos trabalhos filho.

1
3
2
4
5

Como cada trabalho filho é executado em paralelo, a ordem dos resultados gerados não é garantida.

Desempenho do trabalho de thread

Os trabalhos de thread são mais rápidos e leves do que outros tipos de trabalho. Mas eles ainda têm despesas gerais que podem ser grandes quando comparadas ao trabalho que o trabalho está fazendo.

O PowerShell executa comandos e scripts em uma sessão. Apenas um comando ou script pode ser executado de cada vez em uma sessão. Portanto, ao executar vários trabalhos, cada trabalho é executado em uma sessão separada. Cada sessão contribui para a sobrecarga.

Os trabalhos de thread fornecem o melhor desempenho quando o trabalho que executam é maior do que a sobrecarga da sessão usada para executar o trabalho. Há dois casos que cumprem este critério.

  • O trabalho é intensivo em computação - Executar um script em vários trabalhos de thread pode tirar proveito de vários núcleos de processador e concluir mais rapidamente.

  • O trabalho consiste em espera significativa - Um script que passa o tempo esperando por resultados de E/S ou chamadas remotas. A execução em paralelo geralmente é concluída mais rapidamente do que se executada sequencialmente.

(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

O primeiro exemplo acima mostra um loop foreach que cria 1000 trabalhos de thread para fazer uma gravação de cadeia de caracteres simples. Devido à sobrecarga de trabalho, leva mais de 36 segundos para ser concluído.

O segundo exemplo executa o ForEach cmdlet para fazer as mesmas 1000 operações. Desta vez, ForEach-Object é executado sequencialmente, em um único thread, sem qualquer sobrecarga de trabalho. Ele se completa em apenas 7 milissegundos.

No exemplo a seguir, até 5000 entradas são coletadas para 10 logs de sistema separados. Como o script envolve a leitura de vários logs, faz sentido fazer as operações em paralelo.

$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

O script é concluído na metade do tempo quando os trabalhos são executados em paralelo.

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

Trabalhos de thread e variáveis

Há várias maneiras de passar valores para os trabalhos baseados em threads.

Start-ThreadJob pode aceitar variáveis que são canalizadas para o cmdlet, passadas para o bloco de script por meio da palavra-chave $using ou passadas por meio do parâmetro 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 aceita variáveis canalizadas e variáveis passadas diretamente para o bloco de script através da $using palavra-chave.

$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

Como os trabalhos de thread são executados no mesmo processo, qualquer tipo de referência variável passado para o trabalho deve ser tratado com cuidado. Se não for um objeto seguro de thread, ele nunca deve ser atribuído e o método e as propriedades nunca devem ser invocados nele.

O exemplo a seguir passa um objeto .NET ConcurrentDictionary thread-safe para todos os trabalhos filho para coletar objetos de processo nomeados exclusivamente. Como é um objeto seguro para threads, ele pode ser usado com segurança enquanto os trabalhos são executados simultaneamente no processo.

$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

Consulte também