更新 -この回答では、PowerShellランスペースのプロセスとメカニズム、およびそれらがマルチスレッドの非シーケンシャルワークロードに役立つ方法について説明していますが、PowerShellの愛好家であるWarren 'Cookie Monster' F氏は、同じ概念を1つのツールに統合しました呼び出されます -以下に説明することを行い、それから彼はログ用のオプションのスイッチとインポートされたモジュールを含む準備されたセッション状態でそれを拡張しました、本当にクールなもの- あなた自身の光沢のあるソリューションを構築する前にそれをチェックすることを強くお勧めします!Invoke-Parallel
並列実行スペースの実行:
逃げられない待ち時間の削減
元の特定のケースでは、呼び出された実行可能ファイルには/nowait
、ジョブ(この場合は時刻の再同期)が単独で終了している間、呼び出しスレッドのブロックを防ぐオプションがあります。
これにより、発行者の観点からは全体の実行時間が大幅に短縮されますが、各マシンへの接続は引き続き順番に行われます。タイムアウト待機の累積により、数千のクライアントに順番に接続すると、何らかの理由でアクセスできないマシンの数に応じて長い時間がかかる場合があります。
単一またはいくつかの連続したタイムアウトが発生した場合に後続のすべての接続をキューに入れる必要を回避するために、コマンドを接続して起動するジョブを個別のPowerShell実行スペースにディスパッチし、並行して実行できます。
ランスペースとは何ですか?
A の実行空間は、仮想コンテナであなたのPowerShellのコードが実行され、そして表し/ PowerShellの声明/コマンドの視点から環境を保持しています。
大まかに言うと、1 Runspace = 1実行スレッドなので、PowerShellスクリプトを「マルチスレッド化」するのに必要なのは、並列に実行できるRunspaceのコレクションだけです。
元の問題と同様に、複数の実行スペースをコマンドを呼び出すジョブは、次のように分類できます。
- RunspacePoolの作成
- PowerShellスクリプトまたは同等の実行可能コードをRunspacePoolに割り当てる
- 非同期でコードを呼び出します(つまり、コードが戻るのを待つ必要はありません)
RunspacePoolテンプレート
PowerShellには、[RunspaceFactory]
Runspaceコンポーネントの作成を支援するというタイプアクセラレータがあります。それを機能させましょう
1. RunspacePoolとOpen()
それを作成します:
$RunspacePool = [runspacefactory]::CreateRunspacePool(1,8)
$RunspacePool.Open()
2つの引数はに渡されCreateRunspacePool()
、1
そして8
最小と私たち有効与え、任意の時点で実行を許可実行空間の最大数である最大並列度 8を。
2. PowerShellのインスタンスを作成し、実行可能コードを添付してRunspacePoolに割り当てます。
PowerShellのインスタンスは、powershell.exe
プロセス(実際にはホストアプリケーション)と同じではなく、実行するPowerShellコードを表す内部ランタイムオブジェクトです。我々は使用することができます[powershell]
PowerShellの中に新しいPowerShellのインスタンスを作成するために、型アクセラレータを:
$Code = {
param($Credentials,$ComputerName)
$session = New-PSSession -ComputerName $ComputerName -Credential $Credentials
Invoke-Command -Session $session -ScriptBlock {w32tm /resync /nowait /rediscover}
}
$PSinstance = [powershell]::Create().AddScript($Code).AddArgument($creds).AddArgument("computer1.domain.tld")
$PSinstance.RunspacePool = $RunspacePool
3. APMを使用してPowerShellインスタンスを非同期的に呼び出します。
.NET開発用語で非同期プログラミングモデルとして知られているものを使用して、コマンドの呼び出しをBegin
メソッドに分割し、コードを実行するための「青信号」とEnd
結果を収集するためのメソッドに分けることができます。この場合、フィードバックにはまったく関心がないため(w32tm
とにかく出力を待つことはありません)、最初のメソッドを呼び出すだけで期限を設定できます
$PSinstance.BeginInvoke()
RunspacePoolにまとめる
上記の手法を使用して、新しい接続を作成し、並列実行フローでリモートコマンドを呼び出すという順次反復をラップできます。
$ComputerNames = Get-ADComputer -filter * -Properties dnsHostName |select -Expand dnsHostName
$Code = {
param($Credentials,$ComputerName)
$session = New-PSSession -ComputerName $ComputerName -Credential $Credentials
Invoke-Command -Session $session -ScriptBlock {w32tm /resync /nowait /rediscover}
}
$creds = Get-Credential domain\user
$rsPool = [runspacefactory]::CreateRunspacePool(1,8)
$rsPool.Open()
foreach($ComputerName in $ComputerNames)
{
$PSinstance = [powershell]::Create().AddScript($Code).AddArgument($creds).AddArgument($ComputerName)
$PSinstance.RunspacePool = $rsPool
$PSinstance.BeginInvoke()
}
CPUに8つの実行スペースすべてを一度に実行する能力があると仮定すると、実行時間が大幅に短縮されることがわかりますが、使用される「高度な」メソッドによりスクリプトが読みやすくなります。
最適な視差の度合いを判断する:
100個のランスペースを同時に実行できるRunspacePoolを簡単に作成できます。
[runspacefactory]::CreateRunspacePool(1,100)
しかし、結局のところ、すべては、ローカルCPUが処理できる実行単位の数に帰着します。つまり、コードが実行されている限り、論理プロセッサがコードの実行をディスパッチするよりも多くの実行スペースを許可しても意味がありません。
WMIのおかげで、このしきい値は簡単に決定できます。
$NumberOfLogicalProcessor = (Get-WmiObject Win32_Processor).NumberOfLogicalProcessors
[runspacefactory]::CreateRunspacePool(1,$NumberOfLogicalProcessors)
一方、実行中のコード自体がネットワーク遅延などの外部要因により多くの待機時間を要する場合、論理プロセッサよりも多くの同時実行スペースを実行することでメリットが得られるため、おそらくテストする必要があります。範囲の可能な最大の発見する実行空間損益分岐:
foreach($n in ($NumberOfLogicalProcessors..($NumberOfLogicalProcessors*3)))
{
Write-Host "$n: " -NoNewLine
(Measure-Command {
$Computers = Get-ADComputer -filter * -Properties dnsHostName |select -Expand dnsHostName -First 100
...
[runspacefactory]::CreateRunspacePool(1,$n)
...
}).TotalSeconds
}