about_Thread_Jobs

Description courte

Fournit des informations sur les travaux basés sur des threads PowerShell. Un travail de thread est un type de travail en arrière-plan qui exécute une commande ou une expression dans un thread distinct dans le processus de session actuel.

Description longue

PowerShell exécute simultanément des commandes et des scripts via des travaux. Il existe trois types de travaux fournis par PowerShell pour prendre en charge la concurrence.

  • RemoteJob - Les commandes et les scripts s’exécutent dans une session distante. Pour plus d’informations, consultez about_Remote_Jobs.
  • BackgroundJob - Les commandes et les scripts s’exécutent dans un processus distinct sur l’ordinateur local. Pour plus d’informations, consultez à propos des_tâches.
  • PSTaskJob ou ThreadJob - Les commandes et les scripts s’exécutent dans un thread distinct au sein du même processus sur l’ordinateur local.

Les travaux basés sur des threads ne sont pas aussi robustes que les travaux distants et en arrière-plan, car ils s’exécutent dans le même processus sur différents threads. Si un travail a une erreur critique qui bloque le processus, tous les autres travaux du processus sont arrêtés.

Toutefois, les travaux basés sur des threads nécessitent moins de surcharge. Ils n’utilisent pas la couche de communication à distance ou la sérialisation. Les objets de résultat sont retournés en tant que références aux objets en direct dans la session active. Sans cette surcharge, les travaux basés sur des threads s’exécutent plus rapidement et utilisent moins de ressources que les autres types de travaux.

Important

La session parente qui a créé le travail surveille également l’état du travail et collecte les données de pipeline. Le processus enfant du travail est arrêté par le processus parent une fois que le travail atteint un état terminé. Si la session parente est arrêtée, tous les travaux enfants en cours d’exécution sont arrêtés avec leurs processus enfants.

Il existe deux façons de contourner cette situation :

  1. Permet Invoke-Command de créer des travaux qui s’exécutent dans des sessions déconnectées. Pour plus d’informations, consultez about_Remote_Jobs.
  2. Permet Start-Process de créer un processus plutôt qu’un travail. Pour plus d’informations, consultez Start-Process.

Guide pratique pour démarrer et gérer des travaux basés sur des threads

Il existe deux façons de démarrer des travaux basés sur des threads :

  • Start-ThreadJob- à partir du module ThreadJob
  • ForEach-Object -Parallel -AsJob - La fonctionnalité parallèle a été ajoutée dans PowerShell 7.0

Utilisez les mêmes applets de commande de travail décrites dans about_Jobs pour gérer les travaux basés sur des threads.

Utilisation de Start-ThreadJob

Le module ThreadJob est d’abord fourni avec PowerShell 6. Il peut également être installé à partir de PowerShell Gallery pour Windows PowerShell 5.1.

Pour démarrer un travail de thread sur l’ordinateur local, utilisez l’applet Start-ThreadJob de commande avec une commande ou un script placé entre accolades ({ }).

L’exemple suivant démarre un travail de thread qui exécute une Get-Process commande sur l’ordinateur local.

Start-ThreadJob -ScriptBlock { Get-Process }

La Start-ThreadJob commande retourne un ThreadJob objet qui représente le travail en cours d’exécution. L’objet de travail contient des informations utiles sur le travail, y compris son état d’exécution actuel. Il collecte les résultats du travail au fur et à mesure que les résultats sont générés.

Utilisation de ForEach-Object -Parallel -AsJob

PowerShell 7.0 a ajouté un nouveau jeu de paramètres à l’applet de ForEach-Object commande. Les nouveaux paramètres vous permettent d’exécuter des blocs de script dans des threads parallèles en tant que travaux PowerShell.

Vous pouvez diriger les données vers ForEach-Object -Parallel. Les données sont transmises au bloc de script exécuté en parallèle. Le -AsJob paramètre crée des objets de travaux pour chacun des threads parallèles.

La commande suivante démarre un travail qui contient des travaux enfants pour chaque valeur d’entrée redirigée vers la commande. Chaque travail enfant exécute la Write-Output commande avec une valeur d’entrée redirigée comme argument.

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

La ForEach-Object -Parallel commande retourne un objet qui contient des PSTaskJob travaux enfants pour chaque valeur d’entrée redirigée. L’objet de travail contient des informations utiles sur l’état d’exécution des travaux enfants. Il collecte les résultats des travaux enfants à mesure que les résultats sont générés.

Comment attendre la fin et la récupération des résultats d’un travail

Vous pouvez utiliser des applets de commande de travail PowerShell, telles que Wait-Job et Receive-Job pour attendre la fin d’un travail, puis retourner tous les résultats générés par le travail.

La commande suivante démarre un travail de thread qui exécute une Get-Process commande, puis attend la fin de la commande, puis retourne enfin tous les résultats de données générés par la commande.

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

La commande suivante démarre un travail qui exécute une Write-Output commande pour chaque entrée redirigée, puis attend que tous les travaux enfants se terminent, puis retourne tous les résultats de données générés par les travaux enfants.

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

L’applet Receive-Job de commande retourne les résultats des travaux enfants.

1
3
2
4
5

Étant donné que chaque travail enfant s’exécute en parallèle, l’ordre des résultats générés n’est pas garanti.

Performances du travail de thread

Les travaux de thread sont plus rapides et plus légers que d’autres types de travaux. Mais ils ont toujours une surcharge qui peut être importante par rapport au travail que fait le travail.

PowerShell exécute des commandes et un script dans une session. Une seule commande ou script peut s’exécuter à la fois dans une session. Par conséquent, lors de l’exécution de plusieurs travaux, chaque travail s’exécute dans une session distincte. Chaque session contribue à la surcharge.

Les travaux de thread fournissent les meilleures performances lorsque le travail qu’ils effectuent est supérieur à la surcharge de la session utilisée pour exécuter le travail. Il existe deux cas pour répondre à ces critères.

  • Le travail est gourmand en ressources de calcul : l’exécution d’un script sur plusieurs travaux de thread peut tirer parti de plusieurs cœurs de processeur et se terminer plus rapidement.

  • Le travail se compose d’une attente importante : script qui passe du temps à attendre les résultats des E/S ou des appels distants. L’exécution en parallèle se termine généralement plus rapidement qu’en cas d’exécution séquentielle.

(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

Le premier exemple ci-dessus montre une boucle foreach qui crée 1 000 travaux de thread pour effectuer une écriture de chaîne simple. En raison de la surcharge du travail, il faut plus de 36 secondes.

Le deuxième exemple exécute l’applet ForEach de commande pour effectuer les mêmes opérations 1000. Cette fois, ForEach-Object s’exécute de manière séquentielle sur un thread unique, sans surcharge de travail. Il se termine en seulement 7 millisecondes.

Dans l’exemple suivant, jusqu’à 5 000 entrées sont collectées pour 10 journaux système distincts. Étant donné que le script implique la lecture d’un certain nombre de journaux, il est judicieux d’effectuer les opérations en parallèle.

$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

Le script se termine en deux fois lorsque les travaux sont exécutés en parallèle.

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

Travaux et variables de thread

Il existe plusieurs façons de passer des valeurs dans les travaux basés sur des threads.

Start-ThreadJobpeut accepter les variables qui sont redirigées vers l’applet de commande, transmises au bloc de script via le $using mot clé ou transmises via le paramètre 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 -Parallelaccepte le canal dans les variables et les variables transmises directement au bloc de script via le $using mot clé.

$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

Étant donné que les travaux de thread s’exécutent dans le même processus, tout type de référence de variable passé dans le travail doit être traité avec soin. S’il ne s’agit pas d’un objet thread sécurisé, il ne doit jamais être affecté, et la méthode et les propriétés ne doivent jamais être appelées dessus.

L’exemple suivant transmet un objet .NET ConcurrentDictionary thread-safe à tous les travaux enfants pour collecter des objets de processus nommés de manière unique. Étant donné qu’il s’agit d’un objet thread sécurisé, il peut être utilisé en toute sécurité pendant que les travaux s’exécutent simultanément dans le processus.

$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

Voir aussi