SE の雑記

SQL Server の情報をメインに Microsoft 製品の勉強内容を日々投稿

PowerShell の Foreach-Object の Parallel パラメーターの情報

leave a comment

PowerShell は 7.0 以降で、Foreach-Object で Parallel パラメーターが使用できるようになりました。

従来までは、複数スレッドで処理をする場合は、Windows PowerShell ワークフロー / ジョブ / Runspace などを使用するケースがありました。(Windows PowerShell ワークフローは 5.1 までしか使用できないので、現時点で利用することはほとんどない気もしますが)

PowerShell 7.0 以降では、Foreach-Object の Parallel パラメーターを使用することで複数スレッドの処理をシンプルに記述することができるようになっています。

$SampleText = "PowerShell Foreach-Object Parallel"
Clear-Host
Measure-Command {
    1..50 | Foreach-Object {
        $text = $SampleText
        Write-Host ("{0} : {1}" -f $text, $_)
    }
}

Measure-Command {
    1..50 | Foreach-Object -ThrottleLimit 5 -Parallel {
        $text = $using:SampleText
        Write-Host ("{0} : {1}" -f $text, $_)
    }
}

 

PowerShell ForEach-Object Parallel Feature には次のように記載されています。

However, there is still quite a bit of overhead to run script blocks in parallel. Script blocks run in a context called a PowerShell runspace.

シンプルな処理では、スクリプトブロックを並行で実行する際のオーバーヘッドにより、Parallel を使用しない方が処理が短時間で実行されるケースもありますので、どのような処理を並行で実行するかについては注意しておく必要がありますが、1 回の実行に数秒かかる処理などは並行実行すると効果があるのではないでしょうか。

Azure Functions の PowerShell ランタイムも PowerShell のバージョンが 7.0 になっていますので、Azure 上で関数を実行する場合にも効果的に動作するケースがあるかと思います。(Azure Automation については How can we improve Azure Automation service? に記載されていますが、CY21 Q4 に、PowerSHell 7.0 をサポート予定のようです)

Foreach-Object で Parallel パラメーターを使用したした際の情報ですが、冒頭に記載したリリース時の情報だけでなく、docs のドキュメントでいくつかの情報が公開されていますので、どのような情報があるかをまとめておきたいと思います。

Foreach-Object のParallel についてのドキュメント

基本情報

PowerShell についてのドキュメントですが、https://docs.microsoft.com/ja-jp/powershell/scripting/learn/ps101/00-introduction?view=powershell-7.1 をはじめとして、「PoweShell の取得」のドキュメントツリーに様々な情報が公開されています。

image

この中に次のドキュメントがあり、Parallel パラメーターを使用した Foreach-Object についての解説が行われています。

「using:」ステートメントを使用して、親スコープの変数を使用する方法や、Foreach-Object Parallel を「-AsJob」を使用してジョブとして実行し、並行実行しているスクリプトブロックの進行状況を取得する方法が記載されています。

他にも ForEach-Object の解説にも Parallel パラメーターの情報が記載されていますので、基本的な利用方法については、この情報からも確認することができます。

エラーハンドリング

Parallel パラメーターのスクリプトブロックで発生した例外のエラーハンドリングが悩ましいところです。

Clear-Host

try{
    1..10 | Foreach-Object -ThrottleLimit 5 -Parallel {
        Write-Host ("{0}" -f $_)
        if($_ % 2 -eq 0){
            throw "Exception"
        }
    }
}catch{
    Write-Host "Catch Exception"
}

このようなスクリプトを実行しても親スコープでは Exception を拾うことができません。

エラーハンドリングについては、次のような情報が公開されています。

シンプルなのは、ErrorValiable を使用する方法なのでしょうかね。

Clear-Host

1..10 | Foreach-Object -ThrottleLimit 5 -Parallel {
    Write-Host ("{0}" -f $_)
    if($_ % 2 -eq 0){
        throw ("{0} Exception" -f $_)
    }    
} -ErrorVariable errorInfo

foreach ($e in $errorInfo){
    Write-Host $e
}

 

ErrorVariable に指定した変数にスクリプトブロック内で発生した例外が「System.Management.Automation.ErrorRecord」として格納されているので、この情報から親スコープでエラーハンドリングを実施することができます。

$using を使用して親スコープの変数を書き換える方法になるので、使おうか悩ましいのですが、次のような方法でもエラーハンドリングはできるようです。

Clear-Host
$l = New-Object System.Collections.ArrayList
1..10 | Foreach-Object -ThrottleLimit 5 -Parallel {
    Write-Host ("{0}" -f $_)
    try{
        if($_ % 2 -eq 0){
            throw ("{0} Exception" -f $_)
        }        
    }catch{
        $info = $using:l
        $info.Add($Error[0])
    }
} -ErrorVariable errorInfo

foreach ($e in $l){
    Write-Host $e
}

 

まとめ

私はメインで使用しているスクリプトが PowerShell なので、Foreach-Object で Parallel パラメーターが使用できるようになって、簡単に複数のスレッドの処理が記載できるようになったのがとても助かっています。

PowerShell 7.0 より前は、DB に対して複数のクエリを平行に実行したい場合に、Runspace をどうやって作るんだっけというところから思い出していたのですが、7.0 以降は Foreach-Object でシンプルに処理が記載できるので助かっています。(Foreach-Object の Parallel パラメーターも Runspace を使用しているようですが、作成部分がラップされているので、サクッと書けて楽です)

Parallel パラメーターのスクリプトブロックで、親スコープで定義した関数を呼び出すのが私の理解度では難しく、ロジックの再利用性の高いスクリプトをどうやって作るかが悩ましいのですが、Foreach-Object の Parallel パラメーターが活躍できるケースが今後出てくるのではないでしょうか。

Written by Masayuki.Ozawa

4月 20th, 2021 at 10:48 am

Posted in PowerShell

Tagged with

Leave a Reply

Share via
Copy link
Powered by Social Snap