PowerShell中Job相关命令及并行执行任务详解

2019-09-30 09:26:52刘景俊

$job = Start-Job -ScriptBlock { sleep 5; Write-Host "Hello world."; }
Wait-Job $job
Receive-Job -Job $job

把上面的代码保存到文件 mytask.ps1 中执行:

Receive-Job 命令输出了我们在后台执行的任务的 output。

在后台执行多个任务并等待结束

因为 Start-Job 命令是非阻塞的,所以理论上我们可以执行任意多次从而启动很多的后台任务。和等待单个任务相同,仍然可以使用 Wait-Job 命令来等待所有的任务结束,不过此时需要配合 Get-Job 命令一起使用:

> Get-Job | Wait-Job

更常用的方式是我们在 while 循环中不断的检查任务的状态,当所有任务的状态都是 "Completed" 时表示全部任务执行结束:

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

把上面的代码保存到 mytask.ps1 文件中并执行:

代码中我们给每个任务起了名字,并在 while 循环中不断的使用 Get-Job 命令检查任务当前的状态,如果发现任务的状态为 "Completed",就通过 Remove-Job 命令删除它,并在删除前打印任务的名称和 output。

封装一个执行后台任务的函数

下面我们用封装一个简单的函数来并行执行多个任务:

function Run-Tasks
{
 Param
 (
  $taskArr,
  $parallelcount=1
 )
 #测试计时开始
 $startTime = (Get-Date)
  #移除本次会话中已有的所有后台任务
 Remove-Job *
 # 使用变量 $taskCount 保存还没有执行完成的任务数
 $taskCount = $taskArr.Length
 
 #判断设定的并行任务数是否超过当前任务队列中的任务数
 if($parallelCount -gt $taskArr.Length)
 {
  $parallelCount = $taskArr.Length
 }
 #启动初始任务
 foreach($i in 1..$parallelCount)
 {
  Start-Job $taskArr[$i - 1] -Name "task$i"
 }
 #初始任务完成后开始的任务
 $nextIndex = $parallelCount
 #当任务队列中还有任务时不断轮询已建立的任务,当一个后台任务结束时删除这个任务,
 #然后从任务队列中取出下一个任务进行执行,然后等待所有任务执行完成。
 while(($nextIndex -lt $taskArr.Length) -or ($taskCount -gt 0))
 {
  foreach($job in Get-Job)
  {
   $state = [string]$job.State
   if($state -eq "Completed")
   { 
    Write-Host($job.Name + " 已经完成,结果如下:")
    Receive-Job $job
    Remove-Job $job
    $taskCount--
    if($nextIndex -lt $taskArr.Length)
    { 
     $taskNumber = $nextIndex + 1
     Start-Job $taskArr[$nextIndex] -Name "task$taskNumber"
     $nextIndex++
    }
   }
  }
  sleep 1
 }
 "所有任务已完成"
 #得出任务运行的时间
 (New-TimeSpan $startTime).totalseconds
}