Share via


about_Thread_Jobs

Descrição breve

Fornece informações sobre trabalhos baseados em thread 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 simultaneamente comandos e scripts por meio de trabalhos. Há três tipos de trabalho fornecidos pelo PowerShell para oferecer suporte à simultaneidade.

  • RemoteJob - Comandos e scripts 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, consulte 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 thread não são tão robustos quanto os trabalhos 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 thread exigem menos sobrecarga. Eles não usam a camada 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 thread 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 quando o trabalho atinge um estado concluído. Se a sessão pai for encerrada, todos os trabalhos filho em execução serão encerrados junto com seus processos filho.

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

  1. Use Invoke-Command para criar trabalhos 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 saber mais, confira Start-Process.

Como iniciar e gerenciar trabalhos baseados em thread

Há duas maneiras de iniciar trabalhos baseados em thread:

  • 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 thread.

Usando 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 incluído em 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.

Usando 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 o 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. Ele coleta os resultados dos trabalhos infantis à medida que os resultados são 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 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 paralelamente, 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 trabalhos. 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 por 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 eles executam é maior do que a sobrecarga da sessão usada para executar o trabalho. Há dois casos para os quais se enquadram nesse critério.

  • O trabalho é intensivo em computação - A execução de 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 uma espera significativa - Um script que gasta tempo aguardando os resultados de E/S ou chamadas remotas. A execução em paralelo geralmente é concluída mais rapidamente do que se fosse 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 nenhuma 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

Tarefas e variáveis de thread

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

Start-ThreadJob pode aceitar variáveis que são canalizadas para o cmdlet, passadas para o bloco de script por meio da $using palavra-chave 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 por meio 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 de variável passado para o trabalho deve ser tratado cuidadosamente. Se não for um objeto thread safe, 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 thread safe, 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

Confira também