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
ouThreadJob
- 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 :
- 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. - 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 ThreadJobForEach-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-ThreadJob
peut 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 -Parallel
accepte 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