PowerShellの並列処理:RunspacePoolとConcurrentDictionaryで進行状況を管理

PowerShell

はじめに

PowerShellの並列処理サンプルここでは、RunspacePoolを使用して並列処理を実現し、非ジェネリックなHashtableではなく、スレッドセーフなConcurrentDictionaryを使用する方法について解説する。

スクリプトの概要

このスクリプトは、3つのカテゴリー(車、動物、植物)の単語リストを並列で処理し、各カテゴリーの単語数をカウントするものである。以下のリンクからスクリプトの全体を確認できる: GitHub リンク

1. 同期Dictionaryの作成

まず、スレッドセーフなConcurrentDictionaryを作成する。これにより、ジョブとメインスクリプト間で情報を安全に共有できる。

Write-Host "1. 同期Dictionaryを作成しています..." -ForegroundColor Yellow
$syncDict = [System.Collections.Concurrent.ConcurrentDictionary[string,object]]::new()
$syncDict["Host"] = $Host
Write-Host "   同期Dictionaryが作成されました。" -ForegroundColor Green
  • ConcurrentDictionary: スレッドセーフな辞書型コレクション。
  • syncDict["Host"]: PowerShellホストへのアクセスを提供するためのプロパティ。

2. RunspacePoolの作成

次に、RunspacePoolを作成して開く。RunspacePoolは、複数のPowerShellインスタンスを効率的に管理するためのオブジェクトである。

Write-Host "2. RunspacePoolを作成しています..." -ForegroundColor Yellow
$runspacePool = [runspacefactory]::CreateRunspacePool(1, 2)
$runspacePool.Open()
Write-Host "   RunspacePoolが作成され、オープンされました。" -ForegroundColor Green
  • RunspacePool: 複数のPowerShellインスタンスを効率的に管理するためのオブジェクト。
  • CreateRunspacePool(1, 2): 最小1、最大2のRunspaceを持つプールを作成。
  • Open(): RunspacePoolを開き、使用可能にする。

3. カテゴリーごとの単語リストを定義

次に、各カテゴリーごとの単語リストを定義する。

Write-Host "3. カテゴリーごとの単語リストを定義しています..." -ForegroundColor Yellow
$categories = @{
    "車" = "トヨタ ホンダ 日産 マツダ スバル 三菱 レクサス スズキ ダイハツ"
    "動物" = "犬 猫 馬 牛 豚 羊 鶏 兎 鼠 象 キリン ライオン トラ クマ パンダ キツネ オオカミ カンガルー コアラ ペンギン イルカ クジラ サメ カバ サイ ゾウ キリン シマウマ カメレオン イグアナ ワニ"
    "植物" = "桜 梅 椿 菊 向日葵 薔薇 蘭 百合 紫陽花 牡丹 菖蒲 杉 松 竹"
}
Write-Host "   単語リストの定義が完了しました。" -ForegroundColor Green
  • カテゴリーごとの単語リスト: 各カテゴリーに対応する単語リストを定義。

4. ジョブの作成と開始

エコージョブのスクリプトブロックを作成し、バックグラウンドジョブとして実行する。

$scriptBlock = {
    param($category, $words, $jobName, $syncDict)
    $syncDict["Host"].UI.WriteVerboseLine("ジョブ $jobName を開始しました。時刻: $(Get-Date -Format 'HH:mm:ss')")
    $wordCount = ($words -split '\s+').Count
    Start-Sleep -Seconds (Get-Random -Minimum 1 -Maximum 3)
    $syncDict["Host"].UI.WriteVerboseLine("ジョブ $jobName を終了しました。時刻: $(Get-Date -Format 'HH:mm:ss')")
    return "ジョブ $jobName の結果: カテゴリー '$category' の単語数は $wordCount です。"
}

Write-Host "4. ジョブを作成して開始しています..." -ForegroundColor Yellow
$jobs = @()
$categories.GetEnumerator() | ForEach-Object {
    $powershell = [powershell]::Create().AddScript($scriptBlock).AddArgument($_.Key).AddArgument($_.Value).AddArgument("Job$($_.Key)").AddArgument($syncDict)
    $powershell.RunspacePool = $runspacePool
    $jobs += @{
        PowerShell = $powershell
        Handle = $powershell.BeginInvoke()
    }
    Write-Host "   ジョブ 'Job$($_.Key)' が作成され、開始されました。" -ForegroundColor Green
}

Write-Host "   すべてのジョブが開始されました。時刻: $(Get-Date -Format 'HH:mm:ss')" -ForegroundColor Green
  • スクリプトブロック: バックグラウンドジョブとして実行されるコード。
  • BeginInvoke(): ジョブを非同期で開始。

5. ジョブの完了を待機しながらリアルタイム出力

ジョブの完了を待ちながら、リアルタイムで出力を表示する。

Write-Host "5. ジョブの完了を待っています..." -ForegroundColor Yellow
while ($jobs.Handle.IsCompleted -contains $false) {
    foreach ($job in $jobs) {
        if ($job.PowerShell.Streams.Verbose.Count -gt 0) {
            $job.PowerShell.Streams.Verbose.ReadAll() | ForEach-Object { Write-Host "   $_" -ForegroundColor Magenta }
        }
    }
    Start-Sleep -Milliseconds 100
}
  • リアルタイム出力: Verboseストリームに出力がある場合、それを読み取って表示。

6. 結果の取得と後処理

ジョブの結果を取得し、後処理を行う。

Write-Host "6. すべてのジョブが完了しました。結果を取得しています..." -ForegroundColor Yellow
$results = @()
foreach ($job in $jobs) {
    $jobName = $job.PowerShell.Commands.Commands[0].Parameters['jobName'].Value
    Write-Host "   $jobName の結果を取得しています..." -ForegroundColor Blue
    $result = $job.PowerShell.EndInvoke($job.Handle)
    $results += $result
    Write-Host "   結果: $result" -ForegroundColor Green
    Write-Host "   PowerShellインスタンスを破棄しています..." -ForegroundColor Blue
    $job.PowerShell.Dispose()
}
  • 結果の取得EndInvoke()メソッドでジョブの結果を取得。
  • リソースの解放Dispose()メソッドでリソースを解放。

7. RunspacePoolのクリーンアップ

最後に、RunspacePoolをクリーンアップする。

Write-Host "7. RunspacePoolをクリーンアップしています..." -ForegroundColor Yellow
$runspacePool.Close()
$runspacePool.Dispose()
Write-Host "   すべてのジョブが完了し、リソースがクリーンアップされました。時刻: $(Get-Date -Format 'HH:mm:ss')" -ForegroundColor Green
  • RunspacePoolのクリーンアップClose()およびDispose()メソッドでリソースを解放。

最終結果の表示

最後に、すべてのジョブの結果を表示する。

Write-Host "`n最終結果:" -ForegroundColor Cyan
$results | ForEach-Object { 
    Write-Host $_ -ForegroundColor White
}

Write-Host "`n説明:" -ForegroundColor Cyan
Write-Host "上記の結果は、各ジョブが個別のカテゴリー(車、動物、植物)の単語リストを処理し、その単語数をカウントした結果を示している。" -ForegroundColor White
Write-Host "この例では、3つの異なるカテゴリーが並列で処理され、それぞれの単語数が計算された。" -ForegroundColor White
Write-Host "これにより、並列処理の効果と各カテゴリーの単語数を明確に確認できる。" -ForegroundColor White

最終結果

このスクリプトは、RunspacePoolを使用して並列処理を行うサンプルです。
3つのカテゴリー(車、動物、植物)の単語リストを並列で処理し、各カテゴリーの単語数をカウントします。
処理の流れと結果がリアルタイムで表示されます。

処理の流れ:
1. 同期Dictionaryの作成
2. RunspacePoolの作成
3. カテゴリーごとの単語リストの定義
4. ジョブの作成と開始
5. ジョブの完了待機とリアルタイム出力
6. 結果の取得と表示
7. リソースのクリーンアップ

1. 同期Dictionaryを作成しています...
同期Dictionaryが作成されました。
2. RunspacePoolを作成しています...
RunspacePoolが作成され、オープンされました。
3. カテゴリーごとの単語リストを定義しています...
単語リストの定義が完了しました。
4. ジョブを作成して開始しています...
ジョブ 'Job植物' が作成され、開始されました。
詳細: ジョブ Job植物 を開始しました。時刻: 22:46:56
ジョブ 'Job車' が作成され、開始されました。
ジョブ 'Job動物' が作成され、開始されました。
すべてのジョブが開始されました。時刻: 22:46:56詳細: ジョブ Job車 を開始しました。時刻: 22:46:56

5. ジョブの完了を待っています...
詳細: ジョブ Job植物 を終了しました。時刻: 22:46:57
詳細: ジョブ Job動物 を開始しました。時刻: 22:46:57
詳細: ジョブ Job動物 を終了しました。時刻: 22:46:58
詳細: ジョブ Job車 を終了しました。時刻: 22:46:58
6. すべてのジョブが完了しました。結果を取得しています...
の結果を取得しています...
結果: ジョブ Job植物 の結果: カテゴリー '植物' の単語数は 14 です。
PowerShellインスタンスを破棄しています...
の結果を取得しています...
結果: ジョブ Job車 の結果: カテゴリー '車' の単語数は 9 です。
PowerShellインスタンスを破棄しています...
の結果を取得しています...
結果: ジョブ Job動物 の結果: カテゴリー '動物' の単語数は 31 です。
PowerShellインスタンスを破棄しています...
7. RunspacePoolをクリーンアップしています...
すべてのジョブが完了し、リソースがクリーンアップされました。時刻: 22:46:58

最終結果:
ジョブ Job植物 の結果: カテゴリー '植物' の単語数は 14 です。
ジョブ Job車 の結果: カテゴリー '車' の単語数は 9 です。
ジョブ Job動物 の結果: カテゴリー '動物' の単語数は 31 です。

説明:
上記の結果は、各ジョブが個別のカテゴリー(車、動物、植物)の単語リストを処理し、その単語数をカウントした結果を示しています。
この例では、3つの異なるカテエリーが並列で処理され、それぞれの単語数が計算されました。
これにより、並列処理の効果と各カテゴリーの単語数を明確に確認できます。

ライセンス:本記事のテキスト/コードは特記なき限り CC BY 4.0 です。引用の際は出典URL(本ページ)を明記してください。
利用ポリシー もご参照ください。

コメント

タイトルとURLをコピーしました