Powershell:简单实现并行任务的脚本

近期需要向上千台服务器安装程序,发送命令,虽然已经将这些服务器分了几十个组,并在每一组中选择一台安装批量部署程序作为中心节点,向该组内服务器发送文件或命令,但我在本地用传统的方式串行地向各组的中心节点发送命令并等待返回结果,仍然浪费了不少时间。看起来,部署规模大了,使用串行实在是有些忙不过来了。于是我开始尝试powershell的并行任务实现方法。

powershell提供了start-job命令,可以在当前会话中启动后台作业。启动后台作业后,该后台作业就不会对当前的会话有什么影响了,即使是执行时间很长的任务,也可以让它在后台默默地去运行了,且运行过程中不会向屏幕输出信息。后台的任务需要使用get-job来获取状态信息,使用wait-job可以等待后台任务执行完成,Receive-Job则可以输出后台任务的运行结果。有了这些信息,我开始写一个简单的能实现并行任务的脚本。 脚本如下,写得比较糙:

    
    #定义6个任务
    $script_block_1 = {sleep 10}
    $script_block_2 = {sleep 16}
    $script_block_3 = {sleep 8}
    $script_block_4 = {sleep 13}
    $script_block_5 = {sleep 7}
    $script_block_6 = {sleep 10}
    
    #将6个任务写入到一个数组中作为任务队列
    $script_array = $script_block_1, $script_block_2, $script_block_3,
                    $script_block_4, $script_block_5, $script_block_6
    
    #设定同时执行的任务数
    $parallel_count = 1
    
    #测试计时开始
    $start_time = (Get-Date)
    
    #移除本次会话中已有的所有后台任务
    Remove-Job *
    
    #启动初始执行的并行任务
    #$parallel_count设定值就是并行任务执行的数量
    $total_task_count = $script_array.Length
    
    #判断设定的并行任务数是否超过当前任务队列中的任务数
    $init_task_count = $parallel_count
    if($init_task_count -gt $total_task_count)
    {
        $init_task_count = $total_task_count
    }
    #启动初始任务
    foreach($i in 1..$init_task_count)
    {
        Start-Job $script_array[$i - 1] -Name "parallel_job_$i"
    }
    
    #初始任务完成后开始的任务
    $next_index = $init_task_count
    
    #当任务队列中还有任务时不断轮询已建立的任务,当一个后台任务结束时删除这个任务ID,然后从任务队列中
    #取出一个任务进行执行,该任务的任务ID为已删除的任务ID
    while($next_index -lt $total_task_count)
    {
        for($i = 1; $i -le $init_task_count; $i++)
        {
            $state = [string](Get-Job "parallel_job_$i").state
            if($state -eq "Completed")
            {
                Remove-Job "parallel_job_$i"
                Start-Job $script_array[$next_index] -Name "parallel_job_$i"
                $next_index++
            }
            #如果任务队列已空,立即跳出对已建立任务的轮询,等待这些任务结束
            if($next_index -ge $total_task_count)
            {
                break
            }
        }
    }
    
    #等待所有后台任务结束
    get-job | wait-job
    "All jobs done!"
    
    #得出任务运行的时间
    (New-TimeSpan $start_time).totalseconds

脚本的开始定义了6个script block,用大括号括起来,为了方便测试,我写了六个不同时间的sleep。随后需要将这六段代码放入到一个$script_array里面定义为任务队列。$parallel_count指定了并行执行的任务数量(请不要设置为0)。 考虑到同时执行的任务完成时间有长有段,在代码的后面加入了对正在执行的任务轮询的部分。当一个后台任务执行完成后就立即从任务队列中取出下一个任务进行执行,不浪费所有任务的执行时间。 脚本的前后加了个执行时间的测试,我把并行任务的数量分别设置为1,3,6,结果该脚本的运行时间分别是:

并行任务数为1时:70.0920091秒
并行任务数为3时:29.3396782秒
并行任务数为6时:21.4452266秒

可以看到,并行确实能减少任务消耗的总时间。 大家可以试一下这段代码,还没有大规模的实践。在真正的实践中,一定会出现更多的问题,还请各位赐教!

评论