ja-JP/about_Thread_Jobs.help.txt
|
TOPIC about_Thread_Jobs 簡単な説明 PowerShell のスレッドベースのジョブに関する情報を提供します。スレッド ジョブは、現在のセッションプロセス内の別のスレッドでコマンドまたは式を 実行する、バックグラウンドジョブの一種です。 詳細な説明 PowerShell は、ジョブを通じてコマンドとスクリプトを同時に実行します。 PowerShell には、同時実行をサポートする 3 種類のジョブタイプがあります。 - RemoteJob - コマンドとスクリプトはリモートセッションで実行されます。 詳細については、about_Remote_Jobs を参照してください。 - BackgroundJob - コマンドとスクリプトはローカルマシン上の別のプロセスで 実行されます。詳細については、about_Jobs を参照してください。 - PSTaskJob または ThreadJob - コマンドとスクリプトはローカルマシン上の 同じプロセス内の別のスレッドで実行されます。 スレッドベースのジョブは、同じプロセス内の異なるスレッドで実行されるため、 リモートジョブやバックグラウンドジョブほど堅牢ではありません。1 つの ジョブにプロセスをクラッシュさせる致命的なエラーが発生すると、そのプロセス 内の他のすべてのジョブが終了します。 ただし、スレッドベースのジョブはオーバーヘッドが少なくて済みます。リモート 処理層やシリアル化を使用しません。結果オブジェクトは、現在のセッション内の ライブオブジェクトへの参照として返されます。このオーバーヘッドがないため、 スレッドベースのジョブは他のジョブタイプよりも高速に実行され、使用する リソースも少なくなります。 重要: ジョブを作成した親セッションは、ジョブの状態を監視し、パイプライン データを収集します。ジョブの子プロセスは、ジョブが完了状態に達すると親 プロセスによって終了されます。親セッションが終了すると、実行中のすべての 子ジョブも、それらの子プロセスとともに終了します。 この状況を回避する方法は 2 つあります。 1. Invoke-Command を使用して、切断されたセッションで実行されるジョブを 作成します。詳細については、about_Remote_Jobs を参照してください。 2. ジョブではなく Start-Process を使用して新しいプロセスを作成します。 詳細については、Start-Process を参照してください。 スレッドベースのジョブを開始および管理する方法 スレッドベースのジョブを開始するには 2 つの方法があります。 - Start-ThreadJob - ThreadJob モジュールから - ForEach-Object -Parallel -AsJob - 並列機能は PowerShell 7.0 で 追加されました スレッドベースのジョブを管理するには、about_Jobs で説明されているのと 同じ Job コマンドレットを使用します。 Start-ThreadJob の使用 ThreadJob モジュールは PowerShell 6 で初めて提供されました。Windows PowerShell 5.1 用に 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 オブジェクトを返します。ジョブオブジェクトには、子ジョブの 実行状態に関する有用な情報が含まれています。結果が生成されるにつれて、 子ジョブの結果を収集します。 ジョブの完了を待機してジョブ結果を取得する方法 Wait-Job や Receive-Job などの PowerShell のジョブコマンドレットを使用 して、ジョブの完了を待機し、ジョブによって生成されたすべての結果を返す ことができます。 次のコマンドは、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 番目の例は、同じ 1000 回の操作を行うために ForEach-Object コマンドレットを 実行します。今回、ForEach-Object はジョブのオーバーヘッドなしで、単一の スレッドで順次実行されます。わずか 7 ミリ秒で完了します。 次の例では、10 個の個別のシステムログに対して最大 5000 エントリが収集 されます。スクリプトは多数のログの読み取りを伴うため、操作を並列で行うのが 理にかなっています。 $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 関連項目 about_Job_Details about_Remote_Jobs about_Thread_Jobs about_PSSessions about_Remote Get-Job Receive-Job Remove-Job Start-Job Stop-Job Wait-Job ---- 原文: PowerShell-Docs (CC BY 4.0) の翻訳 / PSHelpJaJP |