並行執行任務
最近在寫一些powershell指令碼時候遇到一個問題,那就是要解壓十幾個zip檔案,這樣僅執行完解壓操作差不多5min的時間就過去了,嚴重影響了效率,這時就想到了使用多執行緒的方法來執行這個解壓操作,經過學習瞭解到powershell提供了一個Start-Job命令來實現並行執行。接下來對這個命令做一個總結。
一、真實案例
以我之前遇到的問題作為示例來介紹這個命令,先貼出源方案,序列執行。c:\zipfile目錄下有十幾個zip檔案,需要對檔案進行解壓,按照原有的方案是遍歷整個目錄,一次解壓,耗時5min。
foreach ( $i in ls c:\zipfile\*.zip) { .\7z.exe x $i }
利用start-job後的程式碼:
foreach ( $i in ls c:\zipfile\*.zip) { Start-Job -ScriptBlock{ .\7z.exe x $i } }
使用並行執行的方案後,執行時長縮小到了1min。
二、熟練使用start-job及相關配套命令
1、start-job
後臺執行sleep命令,並且為這個任務命名為“sleep”。
Start-Job -ScriptBlock { sleep 10 } -Name sleep
在後臺執行命令前首先將1賦值給$a,然後再去執行輸出$a的操作,最後命名為test,-InitializationScript引數的作用是任務開始前需要執行的。
Start-Job -InitializationScript { $a = 1 } -ScriptBlock { echo $a } -Name test
若需要後臺執行指令碼,使用-FilePath引數即可
Start-Job -FilePath .\a.ps1
2、get-job
獲取當前所有後臺程序的狀態資訊,starte是任務的執行狀態,“Completed”表示任務完成,“Failed”表示任務執行失敗,“Running”表示正在執行的,“Stopped”表示任務停止。
PS C:\> Get-Job Id Name PSJobTypeName State HasMoreData Location Command -- ---- ------------- ----- ----------- -------- ------- 29 Job29 BackgroundJob Completed True localhost mkdir c:\a\b... 31 test1 BackgroundJob Failed False localhost slepp 20
3、stop-job
停止後臺執行的某個任務,下面的例子是後臺執行一個睡20s的任務,得知該任務的id是43,然後停止id=43的任務,再去檢視後臺任務。如果知道後臺任務的名稱,也可以根據名字來停止(引數是-Name)
PS C:\Users\bill> Start-Job -ScriptBlock{ sleep 20} Id Name PSJobTypeName State HasMoreData Location Command -- ---- ------------- ----- ----------- -------- ------- 43 Job43 BackgroundJob Running True localhost sleep 20 PS C:\Users\bill> Stop-Job -id 43 PS C:\Users\bill> Get-Job Id Name PSJobTypeName State HasMoreData Location Command -- ---- ------------- ----- ----------- -------- ------- 43 Job43 BackgroundJob Stopped False localhost sleep 20
4、receive-job
我們在使用start-job後臺執行任務的時候,往往是不會顯示命令的執行結果,此時可以使用receive-job命令檢視它的結果
PS C:\Users\bill> Start-Job -ScriptBlock { echo "1" } -Name "ok" Id Name PSJobTypeName State HasMoreData Location Command -- ---- ------------- ----- ----------- -------- ------- 45 ok BackgroundJob Running True localhost echo "1" PS C:\Users\bill> Receive-Job -Name ok 1
5、wait-job
再回到最初的案例,對檔案解壓完後,需要將各個解壓檔案移動到某個位置,但是使用start-job將每個解壓任務放到後臺,此時系統會接著執行後續的操作,這時出現了檔案還沒有解壓完成,就已經開始對解壓檔案進行操作,導致指令碼異常。針對這個問題需要用到wait-job命令。
PS C:\> Start-Job -ScriptBlock { sleep 5 } -Name s1 Start-Job -ScriptBlock { sleep 6 } -Name s2 Start-Job -ScriptBlock { sleep 7 } -Name s3 echo "satrt" Id Name PSJobTypeName State HasMoreData Location Command -- ---- ------------- ----- ----------- -------- ------- 47 s1 BackgroundJob Running True localhost sleep 5 49 s2 BackgroundJob Running True localhost sleep 6 51 s3 BackgroundJob Running True localhost sleep 7 satrt
我們可以看到三個後臺任務和輸出“start”幾乎是同時執行的,加入wait-job後
PS C:\> Start-Job -ScriptBlock { sleep 5 } -Name s1 Start-Job -ScriptBlock { sleep 6 } -Name s2 Start-Job -ScriptBlock { sleep 7 } -Name s3 Wait-Job * echo "satrt" Id Name PSJobTypeName State HasMoreData Location Command -- ---- ------------- ----- ----------- -------- ------- 53 s1 BackgroundJob Running True localhost sleep 5 55 s2 BackgroundJob Running True localhost sleep 6 57 s3 BackgroundJob Running True localhost sleep 7 57 s3 BackgroundJob Completed False localhost sleep 7 55 s2 BackgroundJob Completed False localhost sleep 6 53 s1 BackgroundJob Completed False localhost sleep 5 satrt
加入wait-job命令後的現象是,等待所有的後臺命令執行完成後,才去執行echo “start”。此時就明白了wait-job的作用了,那就是等待執行的任務完成後再去執行其他的。
其中wait-job中-Id和-Name可以指定特定的任務。
若不用wait-job命令可以下面的程式碼來監控後臺任務是否執行完
Remove-Job * #測試計時開始 $start_time = (Get-Date) Start-Job -ScriptBlock { sleep 9; Write-Host "Hello myJob1."; } -Name "myJob1" Start-Job -ScriptBlock { sleep 5; Write-Host "Hello myJob2."; } -Name "myJob2" $taskCount = 2 while($taskCount -gt 0) { foreach($job in Get-Job) { $state = [string]$job.State if($state -eq "Completed") { Write-Host($job.Name + " 已經完成") Receive-Job $job $taskCount-- Remove-Job $job } } sleep 1 } "所有任務已完成" #得出任務執行的時間 (New-TimeSpan $start_time).totalseconds
6、remove-job
刪除後臺執行的任務,前提條件是該後臺任務是停止狀態
PS C:\> Get-Job Id Name PSJobTypeName State HasMoreData Location Command -- ---- ------------- ----- ----------- -------- ------- 53 s1 BackgroundJob Completed False localhost sleep 5 55 s2 BackgroundJob Completed False localhost sleep 6 57 s3 BackgroundJob Completed False localhost sleep 7 59 rm BackgroundJob Completed False localhost sleep 20 PS C:\> Remove-Job -Name rm PS C:\> Get-Job Id Name PSJobTypeName State HasMoreData Location Command -- ---- ------------- ----- ----------- -------- ------- 53 s1 BackgroundJob Completed False localhost sleep 5 55 s2 BackgroundJob Completed False localhost sleep 6 57 s3 BackgroundJob Completed False localhost sleep 7 PS C:\> Remove-Job * PS C:\> Get-Job