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


在 PowerShell 中要執行任務腳本,現在通常使用 Runspace,效率很高;任務比較多時,用 Runspace pool 來執行異步操作,可以控制資源池數量,就像 C# 中的線程池一樣

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

為了對比,我們分別采用同步和異步(多線程)方式,模擬執行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 是一個任務腳本數組,方法內遍歷任務腳本,

通過 PowerShell 對象 $psl 添加執行腳本和參數,返回一個作業對象 $job
通過 Runspace pool 對象 $rsp 控制異步多線程,
通過 $job 的 BeginInvoke 方法提交異步操作,
通過輪詢等待所有作業執行完成(IsCompleted),
通過 $job 的 EndInvoke 獲得執行結果

# 執行異步(多線程)任務
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類庫),在調用端用委托定義執行過程和結果,然后將委托以變量形式傳遞給執行端

$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 文件中

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

下面調用同步方法 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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM