はじめに
PowerShellで複雑なタスクを効率的に処理するためには、非同期処理が必要になることもあるかも。その時に備えて、Runspaceを使用して非同期処理を実現し、非ジェネリックなHashtableではなく、スレッドセーフなConcurrentDictionaryを使用する方法を解説。
スクリプトの概要
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# スクリプトの説明 | |
Write-Host @" | |
このスクリプトは、RunspacePoolを使用してバックグラウンドジョブでエコー処理を行うサンプルです。 | |
ユーザーからの入力を受け付け、ジョブを通じてそのまま返却します。 | |
'exit'と入力することでスクリプトを終了します。 | |
処理の流れ: | |
1. RunspacePoolの作成 | |
2. 同期Dictionaryの作成 | |
3. エコージョブの作成と開始 | |
4. ユーザー入力の受付とジョブとの通信 | |
5. 終了処理とリソースのクリーンアップ | |
"@ -ForegroundColor Cyan | |
# RunspacePoolの作成 | |
# RunspacePoolは複数のPowerShellインスタンスを効率的に管理するためのオブジェクトです | |
Write-Host "1. RunspacePoolを作成しています..." -ForegroundColor Yellow | |
# CreateRunspacePool(1, 1)は最小1、最大1のRunspaceを持つプールを作成します | |
$runspacePool = [runspacefactory]::CreateRunspacePool(1, 1) | |
# Open()メソッドでRunspacePoolを開きます。これにより使用可能になります | |
$runspacePool.Open() | |
Write-Host " RunspacePoolが作成され、オープンされました。" -ForegroundColor Green | |
# 同期Dictionaryの作成 | |
# ConcurrentDictionaryはスレッドセーフな辞書型のコレクションです | |
Write-Host "2. 同期Dictionaryを作成しています..." -ForegroundColor Yellow | |
# new()メソッドで新しいConcurrentDictionaryインスタンスを作成します | |
$syncDict = [System.Collections.Concurrent.ConcurrentDictionary[string,object]]::new() | |
# キーと値のペアを追加します。これらはジョブとメインスクリプト間で情報を共有するために使用されます | |
$syncDict["Input"] = $null # ユーザー入力を格納するためのキー | |
$syncDict["Output"] = $null # ジョブの出力を格納するためのキー | |
$syncDict["Exit"] = $false # スクリプト終了フラグ | |
Write-Host " 同期Dictionaryが作成されました。" -ForegroundColor Green | |
# エコージョブのスクリプトブロック | |
# このスクリプトブロックはバックグラウンドジョブとして実行されます | |
$scriptBlock = { | |
param($syncDict) # syncDictパラメータはメインスクリプトとの通信に使用されます | |
# ジョブのメインループ | |
while (-not $syncDict["Exit"]) { # Exitフラグがfalseの間、ループを続けます | |
if ($syncDict["Input"] -ne $null) { # 新しい入力がある場合 | |
# 入力をそのまま出力にコピーします(エコー処理) | |
$syncDict["Output"] = $syncDict["Input"] | |
$syncDict["Input"] = $null # 入力をクリアします | |
} | |
Start-Sleep -Milliseconds 100 # 短い待機時間を設けてCPU使用率を抑えます | |
} | |
} | |
# ジョブの作成と開始 | |
Write-Host "3. ジョブを作成して開始しています..." -ForegroundColor Yellow | |
# PowerShellインスタンスを作成し、スクリプトブロックと引数を追加します | |
$powershell = [powershell]::Create().AddScript($scriptBlock).AddArgument($syncDict) | |
# 作成したPowerShellインスタンスにRunspacePoolを割り当てます | |
$powershell.RunspacePool = $runspacePool | |
# ジョブを非同期で開始します。handleはジョブの状態を追跡するために使用されます | |
$handle = $powershell.BeginInvoke() | |
Write-Host "エコージョブが開始されました。入力を待っています..." -ForegroundColor Green | |
# メインループ: ユーザー入力の受付とジョブとの通信 | |
while (-not $syncDict["Exit"]) { # Exitフラグがfalseの間、ループを続けます | |
# ユーザーからの入力を受け付けます | |
$input = Read-Host "入力してください('exit'で終了)" | |
if ($input -eq 'exit') { # exitが入力された場合、ループを終了します | |
$syncDict["Exit"] = $true | |
break | |
} | |
# ユーザー入力をジョブに渡します | |
$syncDict["Input"] = $input | |
# ジョブからの出力を待ちます | |
while ($syncDict["Output"] -eq $null) { | |
Start-Sleep -Milliseconds 100 # 短い待機時間を設けてCPU使用率を抑えます | |
} | |
# ジョブからの出力(エコー)を表示します | |
Write-Host "エコー: $($syncDict["Output"])" -ForegroundColor Yellow | |
$syncDict["Output"] = $null # 出力をクリアします | |
} | |
# 終了処理 | |
Write-Host "スクリプトを終了しています..." -ForegroundColor Magenta | |
# ジョブの終了を待ちます | |
# EndInvokeメソッドは非同期ジョブの完了を待ち、結果を取得します(この場合は使用しません) | |
$powershell.EndInvoke($handle) | |
# Disposeメソッドでリソースを解放します | |
$powershell.Dispose() | |
# RunspacePoolのクリーンアップ | |
Write-Host "RunspacePoolをクリーンアップしています..." -ForegroundColor Yellow | |
# CloseメソッドでRunspacePoolを閉じます | |
$runspacePool.Close() | |
# Disposeメソッドでリソースを解放します | |
$runspacePool.Dispose() | |
Write-Host "スクリプトが正常に終了しました。" -ForegroundColor Green |
1. RunspacePoolの作成
まず、RunspacePoolを作成して開く。RunspacePoolは、複数のPowerShellインスタンスを効率的に管理するためのオブジェクトである。
1 2 3 4 |
Write-Host "1. RunspacePoolを作成しています..." -ForegroundColor Yellow $runspacePool = [runspacefactory]::CreateRunspacePool(1, 1) $runspacePool.Open() Write-Host " RunspacePoolが作成され、オープンされました。" -ForegroundColor Green |
- RunspacePool: 複数のPowerShellインスタンスを効率的に管理するためのオブジェクト。
CreateRunspacePool(1, 1)
: 最小1、最大1のRunspaceを持つプールを作成。Open()
: RunspacePoolを開き、使用可能にする。
2. 同期Dictionaryの作成
次に、スレッドセーフなConcurrentDictionaryを作成する。これにより、ジョブとメインスクリプト間で情報を安全に共有できる。
1 2 3 4 5 6 |
Write-Host "2. 同期Dictionaryを作成しています..." -ForegroundColor Yellow $syncDict = [System.Collections.Concurrent.ConcurrentDictionary[string,object]]::new() $syncDict["Input"] = $null $syncDict["Output"] = $null $syncDict["Exit"] = $false Write-Host " 同期Dictionaryが作成されました。" -ForegroundColor Green |
- ConcurrentDictionary: スレッドセーフな辞書型コレクション。
syncDict["Input"]
,syncDict["Output"]
,syncDict["Exit"]
: ジョブとメインスクリプト間で情報を共有するためのキー。
3. エコージョブの作成と開始
エコージョブのスクリプトブロックを作成し、バックグラウンドジョブとして実行する。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
$scriptBlock = { param($syncDict) while (-not $syncDict["Exit"]) { if ($syncDict["Input"] -ne $null) { $syncDict["Output"] = $syncDict["Input"] $syncDict["Input"] = $null } Start-Sleep -Milliseconds 100 } } $powershell = [powershell]::Create().AddScript($scriptBlock).AddArgument($syncDict) $powershell.RunspacePool = $runspacePool $handle = $powershell.BeginInvoke() Write-Host "エコージョブが開始されました。入力を待っています..." -ForegroundColor Green |
- スクリプトブロック: バックグラウンドジョブとして実行されるコード。
BeginInvoke()
: ジョブを非同期で開始。
4. ユーザー入力の受付とジョブとの通信
ユーザーからの入力を受け付け、ジョブに渡してその出力を表示する。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
while (-not $syncDict["Exit"]) { $input = Read-Host "入力してください('exit'で終了)" if ($input -eq 'exit') { $syncDict["Exit"] = $true break } $syncDict["Input"] = $input while ($syncDict["Output"] -eq $null) { Start-Sleep -Milliseconds 100 } Write-Host "エコー: $($syncDict["Output"])" -ForegroundColor Yellow $syncDict["Output"] = $null } |
- ユーザー入力の受付:
Read-Host
でユーザーからの入力を受け付ける。 - ジョブとの通信:
syncDict
を通じてジョブに入力を渡し、出力を受け取る。
5. 終了処理とリソースのクリーンアップ
スクリプトの終了時にリソースを解放する。
1 2 3 4 5 6 |
Write-Host "スクリプトを終了しています..." -ForegroundColor Magenta $powershell.EndInvoke($handle) $powershell.Dispose() $runspacePool.Close() $runspacePool.Dispose() Write-Host "スクリプトが正常に終了しました。" -ForegroundColor Green |
- リソースのクリーンアップ:
Dispose()
メソッドでリソースを解放する。
まとめ
このスクリプトは、PowerShellで非同期処理を実現するための基本的な方法を示している。RunspaceとConcurrentDictionaryを活用することで、効率的かつ安全に複数のタスクを同時に処理することができる。
コメント