在 PowerShell 中要執行任務腳本,如今一般使用 Runspace,效率很高;任務比較多時,用 Runspace pool 來執行異步操做,能夠控制資源池數量,就像 C# 中的線程池同樣shell
================================================數組
爲了對比,咱們分別採用同步和異步(多線程)方式,模擬執行10個任務,而且每一個任務都接收一個參數,執行完成後返回執行結果多線程
================================================異步
同步執行方法 輸入參數 $toexecute 是一個任務腳本數組,方法內遍歷任務腳本,直接經過腳本的 Invoke 方法執行(也能夠建立一個 PowerShell 對象添加腳本,經過該 PowerShell 對象的 Invoke 方法執行),而後輸出執行結果測試
# 執行同步(單線程)任務 function RunJob { param($toexecute) # 遍歷執行全部腳本 [int]$arg = 0 foreach($s in $toexecute) { $result = $s.Invoke($arg++) # 執行帶參數的任務腳本 # 執行結果返回一個含有 Success 屬性的對象 if ($result.Success) { Write-Host (" -> 任務執行成功 " + $result.Data + ",當前線程 " + $result.ThreadId) -ForegroundColor Green } else { Write-Host (" -> 任務執行失敗 " + $result.Data + ",當前線程 " + $result.ThreadId) -ForegroundColor Red } } }
異步執行方法 輸入參數 $toexecute 是一個任務腳本數組,方法內遍歷任務腳本,spa
經過 PowerShell 對象 $psl 添加執行腳本和參數,返回一個做業對象 $job
經過 Runspace pool 對象 $rsp 控制異步多線程,
經過 $job 的 BeginInvoke 方法提交異步操做,
經過輪詢等待全部做業執行完成(IsCompleted),
經過 $job 的 EndInvoke 得到執行結果.net
# 執行異步(多線程)任務 function RunJobAsync { param($toexecute) $rsp = [RunspaceFactory]::CreateRunspacePool(1, 5) #設置資源池中Runspace數量最少和最多 $rsp.Open() $jobs = @() [int]$arg = 0 # 遍歷執行全部腳本 foreach($s in $toexecute) { $psl = [Powershell]::Create() $job = $psl.AddScript($s).AddArgument($arg++) # 添加任務腳本和參數 $job.RunspacePool = $rsp Write-Host $("添加任務... " + $job.InstanceId) $jobs += New-Object PSObject -Property @{ Job = $job PowerShell = $psl Result = $job.BeginInvoke() # 異步執行任務腳本 } } # 輪詢等待任務完成 do { Start-Sleep -seconds 1 $cnt = ($jobs | Where {$_.Result.IsCompleted -ne $true}).Count Write-Host ("運行中的任務數量: " + $cnt) } while ($cnt -gt 0) foreach($r in $jobs) { Write-Host ("任務結果: " + $r.Job.InstanceId) $result = $r.Job.EndInvoke($r.Result) # 取得異步執行結果 # 註銷 PowerShell 對象 $r.PowerShell.Dispose() # 輸出完成的任務腳本 #Write-Output ($result) # 執行結果返回一個含有 Success 屬性的對象 if ($result.Success) { Write-Host (" -> 任務執行成功 " + $result.Data + ",當前線程 " + $result.ThreadId) -ForegroundColor Green } else { Write-Host (" -> 任務執行失敗 " + $result.Data + ",當前線程 " + $result.ThreadId) -ForegroundColor Red } } }
初始化任務腳本,循環建立10個任務腳本,每一個任務經過等待1秒鐘模擬腳本執行,定義一個 PSObject 對象做爲返回結果,其中屬性 Success 表明是否成功(故意設置傳入參數5時的任務失敗),Data 表明執行結果,ThreadId 標識當前線程ID線程
這種方式就像 C# 中的方法委託同樣(PowerShell 使用了大量.NET類庫),在調用端用委託定義執行過程和結果,而後將委託以變量形式傳遞給執行端3d
$toexecute = @() # 任務腳本列表 foreach($i in 1..10) { $toexecute += { param($state) #可接收參數 Start-Sleep -Seconds 1 New-Object PSObject -Property @{ Success = $state -ne 5 # 假設傳入參數5時失敗,其他成功 Data = "結果 $state" # 假設Data是執行結果,帶上傳入參數以區分 ThreadId = [AppDomain]::GetCurrentThreadId() # 當前線程ID } } }
注:PowerShell 對象 AddScript 加載腳本執行,也能夠傳入一個腳本文件路徑,所以每一個任務腳本能夠寫到單獨的 .ps1 文件中code
============================================================================
下面調用同步方法 RunJob,而且測量執行時間
Clear-Host $watch = Measure-Command { RunJob -toexecute $toexecute } $elapsed = [Math]::Round($watch.TotalMilliseconds / 1000.0, 2) Write-Output ("同步執行 "+ $toexecute.Count +" 個任務耗時" + $elapsed + "秒")
不出所料,在1個線程 20512 中執行10個任務,耗時10.06秒
============================================================================
下面調用異步方法 RunJobAsync,而且測量執行時間
Clear-Host $watch = Measure-Command { RunJobAsync -toexecute $toexecute } $elapsed = [Math]::Round($watch.TotalMilliseconds / 1000.0, 2) Write-Output ("異步執行 "+ $toexecute.Count +" 個任務耗時" + $elapsed + "秒")
執行結果以下圖,在5個線程中執行10個任務(差很少每一個線程執行2個任務),耗時僅2.15秒
若是咱們將代碼中 [RunspaceFactory]::CreateRunspacePool(1, 5) 中最大資源數改成10,基本每一個任務都能有1個線程執行,測試耗時就1秒多一點點
============================================================================
寫得有點亂,就當是筆記了
參考資料