在 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秒多一点点
============================================================================
写得有点乱,就当是笔记了
参考资料