並列処理を勉強したかったので、RunspacePoolを使って、PowerShellを使って並列処理を実現するサンプルを作成
スクリプト
スクリプトの概要
以下のスクリプトは、3つのカテゴリー(車、動物、植物)の単語リストを並列で処理し、各カテゴリーの単語数をカウントする。処理の流れと結果がリアルタイムで表示されるため、並列処理の効果を実感できる。
スクリプトの詳細解説
1. 名前空間のインポート
1 |
using namespace System.Collections.Generic |
この行は、必要な名前空間をインポートする。
2. RunspacePoolの作成
1 2 |
$runspacePool = [runspacefactory]::CreateRunspacePool(1, 2) $runspacePool.Open() |
RunspacePoolを作成し、最小1、最大2のRunspaceを持つPoolをオープンする。
3. カテゴリーごとの単語リストの定義
1 2 3 4 5 |
$categories = @{ "車" = "トヨタ ホンダ 日産 マツダ スバル 三菱 レクサス スズキ ダイハツ" "動物" = "犬 猫 馬 牛 豚 羊 鶏 兎 鼠 象 キリン ライオン トラ クマ パンダ キツネ オオカミ カンガルー コアラ ペンギン イルカ クジラ サメ カバ サイ ゾウ キリン シマウマ カメレオン イグアナ ワニ" "植物" = "桜 梅 椿 菊 向日葵 薔薇 蘭 百合 紫陽花 牡丹 菖蒲 杉 松 竹" } |
各カテゴリーの単語リストを定義する。
4. スクリプトブロックの定義
1 2 3 4 5 6 7 8 9 10 11 |
$scriptBlock = { param($category, $words, $jobName, $syncHash) $syncHash.Host.UI.WriteVerboseLine("ジョブ $jobName を開始しました。時刻: $(Get-Date -Format 'HH:mm:ss')") $wordCount = ($words -split '\s+').Count Start-Sleep -Seconds (Get-Random -Minimum 1 -Maximum 3) $syncHash.Host.UI.WriteVerboseLine("ジョブ $jobName を終了しました。時刻: $(Get-Date -Format 'HH:mm:ss')") return "ジョブ $jobName の結果: カテゴリー '$category' の単語数は $wordCount です。" } |
各ジョブで実行されるスクリプトブロックを定義する。
5. ジョブの作成と開始
1 2 3 4 5 6 7 8 9 10 |
$jobs = @() $categories.GetEnumerator() | ForEach-Object { $powershell = [powershell]::Create().AddScript($scriptBlock).AddArgument($_.Key).AddArgument($_.Value).AddArgument("Job$($_.Key)").AddArgument($syncHash) $powershell.RunspacePool = $runspacePool $jobs += @{ PowerShell = $powershell Handle = $powershell.BeginInvoke() } Write-Host " ジョブ 'Job$($_.Key)' が作成され、開始されました。" -ForegroundColor Green } |
各カテゴリーごとにジョブを作成し、開始する。
RunspacePoolの説明
RunspacePoolとは?
RunspacePoolは、複数のRunspace(実行空間)をプールし、それをPowerShellインスタンスに対して割り当てる機能を持ったオブジェクトだ。これにより、複数のスクリプトやコマンドを並行して実行することができる。
利点
- 効率的なリソース管理:
- RunspacePoolを使用することで、システムリソースを効率的に管理できる。複数のRunspaceをプールし、必要に応じて割り当てることで、リソースの無駄を減らす。
- 高速な処理:
- 非同期実行により、複数のタスクを同時に処理できるため、全体の処理時間を短縮できる。特に、I/O待ち時間が発生するタスクに対して有効。
- スケーラビリティ:
- RunspacePoolは、最小および最大のRunspace数を指定して作成できるため、システムの負荷に応じてスケールさせることができる。
他の並列処理方法との比較
- バックグラウンドジョブ:
- PowerShellのバックグラウンドジョブは、簡単に並列処理を実現できるが、ジョブの管理が複雑になることがある。
- RunspacePoolは、より細かい制御と効率的なリソース管理が可能。
- PowerShell Workflow:
- Workflowは、複雑なワークフローを定義するのに適しているが、設定やデバッグが難しいことがある。
- RunspacePoolは、シンプルな並列処理に適しており、デバッグも比較的容易。
- async/await:
- C#などの言語で使用されるasync/awaitは、非同期処理を簡単に実装できるが、PowerShellでは直接サポートされていない。
- RunspacePoolは、PowerShellで非同期処理を実現するための標準的な方法。
ハッシュテーブルを使用した進行状況の追跡
ハッシュテーブルを使用する理由
PowerShellで並列処理を行う際、各スレッドは独自の実行空間を持つため、進行状況を追跡するのが難しくなる。この問題を解決するために、スレッドセーフなデータ構造である同期されたハッシュテーブルを使用する。これにより、複数のスレッドから安全にデータを共有し、進行状況を追跡できる。
具体的な実装部分
1 2 |
$syncHash = [System.Collections.Hashtable]::Synchronized(@{}) $syncHash.Host = $Host |
この部分で、同期されたハッシュテーブルを作成し、ホスト情報を格納する。これにより、各スレッドが進行状況を安全に更新できる。
注意 Write-Hostではジョブの処理結果が出力されない
Write-Hostは、コンソールに直接出力するためのコマンドレットであり、パイプラインにデータを渡すことができない。そのため、ジョブの進行状況や結果を他のスレッドやメインスレッドに渡すことが難しい。代わりに、同期されたハッシュテーブルを使用することで、各スレッドが進行状況を安全に更新し、メインスレッドでその情報を取得できるようにしている。
実行結果の例
スクリプトを実行すると、以下のような結果が得られる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
このスクリプトは、RunspacePoolを使用して並列処理を行うサンプルです。 3つのカテゴリー(車、動物、植物)の単語リストを並列で処理し、各カテゴリーの単語数をカウントします。 処理の流れと結果がリアルタイムで表示されます。 処理の流れ: 1. RunspacePoolの作成 2. カテゴリーごとの単語リストの定義 3. ジョブの作成と開始 4. ジョブの完了待機とリアルタイム出力 5. 結果の取得と表示 6. リソースのクリーンアップ 1. RunspacePoolを作成しています... RunspacePoolが作成され、オープンされました。 2. カテゴリーごとの単語リストを定義しています... 単語リストの定義が完了しました。 3. ジョブを作成して開始しています... ジョブ 'Job植物' が作成され、開始されました。 詳細: ジョブ Job植物 を開始しました。時刻: 18:04:40 ジョブ 'Job車' が作成され、開始されました。詳細: ジョブ Job車 を開始しました。時刻: 18:04:40 ジョブ 'Job動物' が作成され、開始されました。 すべてのジョブが開始されました。時刻: 18:04:40 4. ジョブの完了を待っています... 詳細: ジョブ Job植物 を終了しました。時刻: 18:04:42 詳細: ジョブ Job動物 を開始しました。時刻: 18:04:42 詳細: ジョブ Job車 を終了しました。時刻: 18:04:42 詳細: ジョブ Job動物 を終了しました。時刻: 18:04:43 5. すべてのジョブが完了しました。結果を取得しています... の結果を取得しています... 結果: ジョブ Job植物 の結果: カテゴリー '植物' の単語数は 14 です。 PowerShellインスタンスを破棄しています... の結果を取得しています... 結果: ジョブ Job車 の結果: カテゴリー '車' の単語数は 9 です。 PowerShellインスタンスを破棄しています... の結果を取得しています... 結果: ジョブ Job動物 の結果: カテゴリー '動物' の単語数は 31 です。 PowerShellインスタンスを破棄しています... 6. RunspacePoolをクリーンアップしています... すべてのジョブが完了し、リソースがクリーンアップされました。時刻: 18:04:43 最終結果: ジョブ Job植物 の結果: カテゴリー '植物' の単語数は 14 です。 ジョブ Job車 の結果: カテゴリー '車' の単語数は 9 です。 ジョブ Job動物 の結果: カテゴリー '動物' の単語数は 31 です。 説明: 上記の結果は、各ジョブが個別のカテゴリー(車、動物、植物)の単語リストを処理し、その単語数をカウントした結果を示しています。 この例では、3つの異なるカテゴリーが並列で処理され、それぞれの単語数が計算されました。 これにより、並列処理の効果と各カテゴリーの単語数を明確に確認できます。 |
応用例と改善点
このスクリプトは、他の並列処理タスクにも応用できる。例えば、大量のデータ処理や複数のAPI呼び出しなど。また、エラーハンドリングやログ機能を追加することで、さらに実用的なスクリプトに改善ができそうだ。
参考
マルチスレッド処理中の進行状況の表示 – PowerShell | Microsoft Learn
Hashtable.Synchronized(Hashtable) メソッド (System.Collections) | Microsoft Learn
コメント