PowerShell 中 RunspacePool 執行異步多線程任務

在 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秒多一點點

============================================================================

寫得有點亂,就當是筆記了

 

參考資料

PowerShell runspace 的建立,使用和查錯

Multithreading with PowerShell using RunspacePool

相關文章
相關標籤/搜索