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
ouThreadJob
- 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:
- Use
Invoke-Command
para criar trabalhos que são executados em sessões desconectadas. Para obter mais informações, consulte about_Remote_Jobs. - 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 ThreadJobForEach-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