about_Thread_Jobs
簡単な説明
PowerShell スレッド ベースのジョブに関する情報を提供します。 スレッド ジョブは、現在のセッション プロセス内の別のスレッドでコマンドまたは式を実行するバックグラウンド ジョブの一種です。
長い説明
PowerShell は、ジョブを介してコマンドとスクリプトを同時に実行します。 コンカレンシーをサポートするために PowerShell によって提供されるジョブの種類は 3 種類です。
RemoteJob- コマンドとスクリプトはリモート セッションで実行されます。 詳細については、「about_Remote_Jobs」 を参照してください。BackgroundJob- コマンドとスクリプトは、ローカル コンピューター上の別のプロセスで実行されます。 詳細については、「about_Jobs」を参照してください。PSTaskJobまたはThreadJob- コマンドとスクリプトは、ローカル コンピューター上の同じプロセス内の別のスレッドで実行されます。
スレッド ベースのジョブは、異なるスレッドで同じプロセスで実行されるので、リモート ジョブやバックグラウンド ジョブほど堅牢ではありません。 1 つのジョブでプロセスがクラッシュする重大なエラーが発生した場合、プロセス内の他のすべてのジョブは終了します。
ただし、スレッド ベースのジョブでは、必要なオーバーヘッドが少なくなります。 リモート処理レイヤーやシリアル化は使用しません。 結果オブジェクトは、現在のセッションのライブ オブジェクトへの参照として返されます。 このオーバーヘッドがない場合、スレッド ベースのジョブの実行速度が向上し、他のジョブの種類よりも使用されるリソースが少なくなります。
重要
ジョブを作成した親セッションもジョブの状態を監視し、パイプライン データを収集します。 ジョブの子プロセスは、ジョブが完了状態になると親プロセスによって終了されます。 親セッションが終了すると、実行中のすべての子ジョブが子プロセスと共に終了します。
この状況を回避するには、次の 2 つの方法があります。
- を使用
Invoke-Commandして、切断されたセッションで実行されるジョブを作成します。 詳細については、「about_Remote_Jobs」 を参照してください。 - ジョブ
Start-Processではなく新しいプロセスを作成するには、 を使用します。 詳細については、「 Start-Process」を参照してください。
スレッド ベースのジョブを開始および管理する方法
スレッド ベースのジョブを開始するには、次の 2 つの方法があります。
Start-ThreadJob- ThreadJob モジュールからForEach-Object -Parallel -AsJob- 並列機能は PowerShell 7.0 で追加されました
スレッド ベースの ジョブを 管理するには、 about_Jobsで説明 されているのと同じ Job コマンドレットを使用します。
Start-ThreadJob を使用する
ThreadJob モジュール は、最初に PowerShell 6 に出荷されました。 また、5.1 用の PowerShell ギャラリー からWindows PowerShellインストールできます。
ローカル コンピューターでスレッド ジョブを開始 Start-ThreadJob するには、 コマンドレットを使用し、中かっこ () で囲まれたコマンドまたはスクリプトを使用します{ }。
次の例では、ローカル コンピューターでコマンドを実行するスレッド Get-Process ジョブを開始します。
Start-ThreadJob -ScriptBlock { Get-Process }
コマンド Start-ThreadJob は、実行中のジョブ ThreadJob を表す オブジェクトを返します。 ジョブ オブジェクトには、現在実行中の状態を含む、ジョブに関する有用な情報が含まれている。 結果が生成されると、ジョブの結果が収集されます。
ForEach-Object -Parallel -AsJob を使用する
PowerShell 7.0 では、コマンドレットに新しいパラメーター セットが追加 ForEach-Object されました。 新しいパラメーターを使用すると、PowerShell ジョブとして並列スレッドでスクリプト ブロックを実行できます。
パイプを使用してデータを に接続できます ForEach-Object -Parallel。 データは、並列で実行されるスクリプト ブロックに渡されます。 パラメーター -AsJob は、並列スレッドごとにジョブ オブジェクトを作成します。
次のコマンドは、コマンドにパイプされた各入力値の子ジョブを含むジョブを開始します。 各子ジョブは、パイプされた Write-Output 入力値を引数として使用してコマンドを実行します。
1..5 | ForEach-Object -Parallel { Write-Output $_ } -AsJob
コマンド ForEach-Object -Parallel は、パイプされた各 PSTaskJob 入力値の子ジョブを含む オブジェクトを返します。 ジョブ オブジェクトには、状態を実行している子ジョブに関する有用な情報が含まれている。 結果が生成されると、子ジョブの結果が収集されます。
ジョブが完了するまで待機し、ジョブの結果を取得する方法
や などの PowerShell ジョブ コマンドレットWait-Job``Receive-Jobを使用して、ジョブが完了するまで待機し、ジョブによって生成された結果を返すことができます。
次のコマンドは、 Get-Process コマンドを実行するスレッド ジョブを開始し、コマンドが完了するまで待機し、最後にコマンドによって生成されたデータ結果を返します。
Start-ThreadJob -ScriptBlock { Get-Process } | Wait-Job | Receive-Job
次のコマンド Write-Output は、パイプ処理された各入力に対してコマンドを実行するジョブを開始し、すべての子ジョブが完了するまで待機し、最後に子ジョブによって生成されたデータ結果を返します。
1..5 | ForEach-Object -Parallel { Write-Output $_ } -AsJob | Wait-Job | Receive-Job
コマンドレット Receive-Job は、子ジョブの結果を返します。
1
3
2
4
5
各子ジョブは並列で実行されるので、生成された結果の順序は保証されません。
スレッド ジョブのパフォーマンス
スレッド ジョブは、他の種類のジョブよりも高速で軽量です。 ただし、ジョブが実行している作業と比較して大きいオーバーヘッドが残っています。
PowerShell は、セッションでコマンドとスクリプトを実行します。 セッションで一度に実行できるコマンドまたはスクリプトは 1 つのみです。 そのため、複数のジョブを実行する場合、各ジョブは個別のセッションで実行されます。 各セッションはオーバーヘッドの一部です。
スレッド ジョブは、実行する作業がジョブの実行に使用されるセッションのオーバーヘッドを超える場合に最適なパフォーマンスを提供します。 この条件を満たすケースは 2 つです。
作業はコンピューティング集中型です。複数のスレッド ジョブでスクリプトを実行すると、複数のプロセッサ コアを利用し、より高速に完了できます。
作業は、重大な待機で構成されます。I/O またはリモート呼び出しの結果を待つ時間を費やすスクリプト。 通常、並列実行は、順次実行する場合よりも速く完了します。
(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
上の最初の例は、単純な文字列書き込みを行う 1000 スレッド ジョブを作成する foreach ループを示しています。 ジョブのオーバーヘッドにより、完了まで 36 秒以上かかります。
2 番目の例では、 コマンドレット ForEach を実行して、同じ 1000 操作を実行します。
今回は、ジョブ ForEach-Object のオーバーヘッドなしで、1 つのスレッドで順次実行されます。 わずか 7 ミリ秒で完了します。
次の例では、10 の個別のシステム ログに対して最大 5,000 エントリが収集されます。 スクリプトには多数のログの読み取りが含まれるので、操作を並列で行うのが理にかなっています。
$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
このスクリプトは、ジョブが並列で実行される時間の半分で完了します。
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
スレッド ジョブと変数
スレッド ベースのジョブに値を渡す方法は複数あります。
Start-ThreadJob は、コマンドレットにパイプ処理 $using される変数、キーワードを使用してスクリプト ブロックに渡される変数、または 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 は、パイプで渡された変数と、 キーワードを使用してスクリプト ブロックに直接渡される変数を受け入 $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
スレッド ジョブは同じプロセスで実行されるので、ジョブに渡される変数参照型は慎重に扱う必要があります。 スレッド セーフ オブジェクトではない場合は、割り当ては行う必要があります。また、メソッドとプロパティを呼び出す必要があります。
次の例では、スレッド セーフな .NET ConcurrentDictionary オブジェクトをすべての子ジョブに渡して、一意の名前のプロセス オブジェクトを収集します。 スレッド セーフオブジェクトであるから、ジョブがプロセス内で同時に実行されている間は安全に使用できます。
$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
関連項目
フィードバック
フィードバックの送信と表示