Búsqueda de sitios web

Acerca de los trabajos de hilo


Breve descripción

Proporciona información sobre los trabajos basados en subprocesos de PowerShell. Un trabajo de subproceso es un tipo de trabajo en segundo plano que ejecuta un comando o expresión en un subproceso separado dentro del proceso de sesión actual.

Descripción larga

PowerShell ejecuta simultáneamente comandos y scripts a través de trabajos. PowerShell proporciona tres tipos de trabajos para admitir la simultaneidad.

  • RemoteJob: los comandos y scripts se ejecutan en una sesión remota. Para obtener información, consulte about_Remote_Jobs.
  • BackgroundJob: los comandos y scripts se ejecutan en un proceso separado en la máquina local. Para obtener más información, consulte about_Jobs.
  • PSTaskJob o ThreadJob: los comandos y scripts se ejecutan en un hilo separado dentro del mismo proceso en la máquina local.

Los trabajos basados en subprocesos no son tan sólidos como los trabajos remotos y en segundo plano, porque se ejecutan en el mismo proceso en subprocesos diferentes. Si un trabajo tiene un error crítico que bloquea el proceso, todos los demás trabajos del proceso finalizan.

Sin embargo, los trabajos basados en subprocesos requieren menos gastos generales. No utilizan la capa remota ni la serialización. Los objetos resultantes se devuelven como referencias a objetos activos en la sesión actual. Sin esta sobrecarga, los trabajos basados en subprocesos se ejecutan más rápido y utilizan menos recursos que los otros tipos de trabajos.

es importante

La sesión principal que creó el trabajo también monitorea el estado del trabajo y recopila datos de canalización. El proceso principal finaliza el proceso secundario del trabajo una vez que el trabajo alcanza un estado finalizado. Si finaliza la sesión principal, todos los trabajos secundarios en ejecución finalizan junto con sus procesos secundarios.

Hay dos formas de solucionar esta situación:

  1. Utilice Invoke-Command para crear trabajos que se ejecuten en sesiones desconectadas. Para obtener más información, consulte about_Remote_Jobs.
  2. Utilice Start-Process para crear un nuevo proceso en lugar de un trabajo. Para obtener más información, consulte Inicio del proceso.

Cómo iniciar y gestionar trabajos basados en subprocesos

Hay dos formas de iniciar trabajos basados en subprocesos:

  • Start-ThreadJob - desde el módulo ThreadJob
  • ForEach-Object -Parallel -AsJob: la función paralela se agregó en PowerShell 7.0

Utilice los mismos cmdlets Job descritos en about_Jobs para administrar trabajos basados en subprocesos.

Usando Start-ThreadJob

El módulo ThreadJob se envió por primera vez con PowerShell 6. También se puede instalar desde la Galería de PowerShell para Windows PowerShell 5.1.

Para iniciar un trabajo de subproceso en la computadora local, use el cmdlet Start-ThreadJob con un comando o script encerrado entre llaves ({ }).

El siguiente ejemplo inicia un trabajo de subproceso que ejecuta un comando Get-Process en la computadora local.

Start-ThreadJob -ScriptBlock { Get-Process }

El comando Start-ThreadJob devuelve un objeto ThreadJob que representa el trabajo en ejecución. El objeto del trabajo contiene información útil sobre el trabajo, incluido su estado de ejecución actual. Recopila los resultados del trabajo a medida que se generan los resultados.

Usando ForEach-Object -Parallel -AsJob

PowerShell 7.0 agregó un nuevo conjunto de parámetros al cmdlet ForEach-Object. Los nuevos parámetros le permiten ejecutar bloques de script en subprocesos paralelos como trabajos de PowerShell.

Puede canalizar datos a ForEach-Object -Parallel. Los datos se pasan al bloque de script que se ejecuta en paralelo. El parámetro -AsJob crea objetos de trabajo para cada uno de los subprocesos paralelos.

El siguiente comando inicia un trabajo que contiene trabajos secundarios para cada valor de entrada canalizado al comando. Cada trabajo secundario ejecuta el comando Write-Output con un valor de entrada canalizado como argumento.

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

El comando ForEach-Object -Parallel devuelve un objeto PSTaskJob que contiene trabajos secundarios para cada valor de entrada canalizado. El objeto de trabajo contiene información útil sobre el estado de ejecución de los trabajos secundarios. Recopila los resultados de los trabajos secundarios a medida que se generan los resultados.

Cómo esperar a que se complete un trabajo y recuperar sus resultados

Puede usar cmdlets de trabajo de PowerShell, como Wait-Job y Receive-Job para esperar a que se complete un trabajo y luego devolver todos los resultados generados por el trabajo.

El siguiente comando inicia un trabajo de subproceso que ejecuta un comando Get-Process, luego espera a que se complete el comando y finalmente devuelve todos los resultados de datos generados por el comando.

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

El siguiente comando inicia un trabajo que ejecuta un comando Write-Output para cada entrada canalizada, luego espera a que se completen todos los trabajos secundarios y finalmente devuelve todos los resultados de datos generados por los trabajos secundarios.

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

El cmdlet Receive-Job devuelve los resultados de los trabajos secundarios.

1
3
2
4
5

Dado que cada trabajo secundario se ejecuta en paralelo, no se garantiza el orden de los resultados generados.

Rendimiento del trabajo de hilo

Los trabajos con hilos son más rápidos y livianos que otros tipos de trabajos. Pero todavía tienen gastos generales que pueden ser grandes en comparación con el trabajo que realiza el trabajo.

PowerShell ejecuta comandos y scripts en una sesión. Sólo se puede ejecutar un comando o script a la vez en una sesión. Entonces, cuando se ejecutan varios trabajos, cada trabajo se ejecuta en una sesión separada. Cada sesión contribuye a los gastos generales.

Los trabajos de subprocesos proporcionan el mejor rendimiento cuando el trabajo que realizan es mayor que la sobrecarga de la sesión utilizada para ejecutar el trabajo. Hay dos casos que cumplen con este criterio.

  • El trabajo requiere un uso intensivo de computación: ejecutar un script en múltiples trabajos de subprocesos puede aprovechar múltiples núcleos de procesador y completarse más rápido.

  • El trabajo consiste en una espera significativa: un script que dedica tiempo a esperar los resultados de E/S o de llamadas remotas. La ejecución en paralelo generalmente se completa más rápido que si se ejecuta de forma secuencial.

(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

El primer ejemplo anterior muestra un bucle foreach que crea 1000 trabajos de subprocesos para realizar una escritura de cadena simple. Debido a la sobrecarga del trabajo, se necesitan más de 36 segundos para completarse.

El segundo ejemplo ejecuta el cmdlet ForEach para realizar las mismas 1000 operaciones. Esta vez, ForEach-Object se ejecuta secuencialmente, en un solo subproceso, sin ninguna sobrecarga de trabajo. Se completa en tan solo 7 milisegundos.

En el siguiente ejemplo, se recopilan hasta 5000 entradas para 10 registros del sistema separados. Dado que el script implica leer varios registros, tiene sentido realizar las operaciones en 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

El script se completa en la mitad del tiempo cuando los trabajos se ejecutan en 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

Trabajos de subprocesos y variables

Hay varias formas de pasar valores a los trabajos basados en subprocesos.

Start-ThreadJob puede aceptar variables que se canalizan al cmdlet, se pasan al bloque de script mediante la palabra clave $using o se pasan mediante ArgumentList. fuerte> parámetro.

$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 acepta variables canalizadas y variables pasadas directamente al bloque de secuencia de comandos mediante la palabra clave $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

Dado que los trabajos de subprocesos se ejecutan en el mismo proceso, cualquier tipo de referencia variable que se pase al trabajo debe tratarse con cuidado. Si no es un objeto seguro para subprocesos, nunca se le debe asignar, y nunca se le deben invocar métodos y propiedades.

El siguiente ejemplo pasa un objeto .NET ConcurrentDictionary seguro para subprocesos a todos los trabajos secundarios para recopilar objetos de proceso con nombres únicos. Dado que es un objeto seguro para subprocesos, se puede utilizar de forma segura mientras los trabajos se ejecutan simultáneamente en el proceso.

$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

Ver también

  • about_Job_Details
  • acerca de_trabajos_remotos
  • about_Thread_Jobs
  • about_PSSessions
  • acerca de_Remoto
  • Get-Job
  • Receive-Job
  • Remove-Job
  • Start-Job
  • Stop-Job
  • Wait-Job

Artículos relacionados