在 PowerShell 中要执行任务脚本,现在通常使用 Runspace,效率很高;任务比较多时,用 Runspace pool 来执行异步操作,可以控制资源池数量,就像 C# 中的线程池一样

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

为了对比,我们分别采用同步和异步(多线程)方式,模拟执行10个任务,并且每个任务都接收一个参数,执行完成后返回执行结果

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

同步执行方法    输入参数 $toexecute 是一个任务脚本数组,方法内遍历任务脚本,直接通过脚本的 Invoke 方法执行(也可以创建一个 PowerShell 对象添加脚本,通过该 PowerShell 对象的 Invoke 方法执行),然后输出执行结果

  1. # 执行同步(单线程)任务
  2. function RunJob {
  3. param($toexecute)
  4. # 遍历执行所有脚本
  5. [int]$arg = 0
  6. foreach($s in $toexecute) {
  7. $result = $s.Invoke($arg++) # 执行带参数的任务脚本
  8. # 执行结果返回一个含有 Success 属性的对象
  9. if ($result.Success) {
  10. Write-Host (" -> 任务执行成功 " + $result.Data + ",当前线程 " + $result.ThreadId) -ForegroundColor Green
  11. }
  12. else {
  13. Write-Host (" -> 任务执行失败 " + $result.Data + ",当前线程 " + $result.ThreadId) -ForegroundColor Red
  14. }
  15. }
  16. }

异步执行方法    输入参数 $toexecute 是一个任务脚本数组,方法内遍历任务脚本,

通过 PowerShell 对象 $psl 添加执行脚本和参数,返回一个作业对象 $job
通过 Runspace pool 对象 $rsp 控制异步多线程,
通过 $job 的 BeginInvoke 方法提交异步操作,
通过轮询等待所有作业执行完成(IsCompleted),
通过 $job 的 EndInvoke 获得执行结果

  1. # 执行异步(多线程)任务
  2. function RunJobAsync {
  3. param($toexecute)
  4. $rsp = [RunspaceFactory]::CreateRunspacePool(1, 5) #设置资源池中Runspace数量最少和最多
  5. $rsp.Open()
  6. $jobs = @()
  7. [int]$arg = 0
  8. # 遍历执行所有脚本
  9. foreach($s in $toexecute) {
  10. $psl = [Powershell]::Create()
  11. $job = $psl.AddScript($s).AddArgument($arg++) # 添加任务脚本和参数
  12. $job.RunspacePool = $rsp
  13. Write-Host $("添加任务... " + $job.InstanceId)
  14. $jobs += New-Object PSObject -Property @{
  15. Job = $job
  16. PowerShell = $psl
  17. Result = $job.BeginInvoke() # 异步执行任务脚本
  18. }
  19. }
  20.  
  21. # 轮询等待任务完成
  22. do
  23. {
  24. Start-Sleep -seconds 1
  25. $cnt = ($jobs | Where {$_.Result.IsCompleted -ne $true}).Count
  26. Write-Host ("运行中的任务数量: " + $cnt)
  27. } while ($cnt -gt 0)
  28.  
  29. foreach($r in $jobs) {
  30. Write-Host ("任务结果: " + $r.Job.InstanceId)
  31. $result = $r.Job.EndInvoke($r.Result) # 取得异步执行结果
  32.  
  33. # 注销 PowerShell 对象
  34. $r.PowerShell.Dispose()
  35.  
  36. # 输出完成的任务脚本
  37. #Write-Output ($result)
  38.  
  39. # 执行结果返回一个含有 Success 属性的对象
  40. if ($result.Success) {
  41. Write-Host (" -> 任务执行成功 " + $result.Data + ",当前线程 " + $result.ThreadId) -ForegroundColor Green
  42. }
  43. else {
  44. Write-Host (" -> 任务执行失败 " + $result.Data + ",当前线程 " + $result.ThreadId) -ForegroundColor Red
  45. }
  46. }
  47. }

初始化任务脚本,循环创建10个任务脚本,每个任务通过等待1秒钟模拟脚本执行,定义一个 PSObject 对象作为返回结果,其中属性 Success 代表是否成功(故意设置传入参数5时的任务失败),Data 代表执行结果,ThreadId 标识当前线程ID

这种方式就像 C# 中的方法委托一样(PowerShell 使用了大量.NET类库),在调用端用委托定义执行过程和结果,然后将委托以变量形式传递给执行端

  1. $toexecute = @() # 任务脚本列表
  2. foreach($i in 1..10) {
  3. $toexecute += {
  4. param($state) #可接收参数
  5. Start-Sleep -Seconds 1
  6. New-Object PSObject -Property @{
  7. Success = $state -ne 5 # 假设传入参数5时失败,其余成功
  8. Data = "结果 $state" # 假设Data是执行结果,带上传入参数以区分
  9. ThreadId = [AppDomain]::GetCurrentThreadId() # 当前线程ID
  10. }
  11. }
  12. }

注:PowerShell 对象 AddScript 加载脚本执行,也可以传入一个脚本文件路径,因此每个任务脚本可以写到单独的 .ps1 文件中

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

下面调用同步方法 RunJob,并且测量执行时间

  1. Clear-Host
  2. $watch = Measure-Command {
  3. RunJob -toexecute $toexecute
  4. }
  5. $elapsed = [Math]::Round($watch.TotalMilliseconds / 1000.0, 2)
  6. Write-Output ("同步执行 "+ $toexecute.Count +" 个任务耗时" + $elapsed + "秒")

不出所料,在1个线程 20512 中执行10个任务,耗时10.06秒

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

下面调用异步方法 RunJobAsync,并且测量执行时间

  1. Clear-Host
  2. $watch = Measure-Command {
  3. RunJobAsync -toexecute $toexecute
  4. }
  5. $elapsed = [Math]::Round($watch.TotalMilliseconds / 1000.0, 2)
  6. Write-Output ("异步执行 "+ $toexecute.Count +" 个任务耗时" + $elapsed + "秒")

执行结果如下图,在5个线程中执行10个任务(差不多每个线程执行2个任务),耗时仅2.15秒

如果我们将代码中 [RunspaceFactory]::CreateRunspacePool(1, 5) 中最大资源数改为10,基本每个任务都能有1个线程执行,测试耗时就1秒多一点点

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

写得有点乱,就当是笔记了

参考资料

PowerShell runspace 的创建,使用和查错

Multithreading with PowerShell using RunspacePool

PowerShell 中 RunspacePool 执行异步多线程任务的更多相关文章

  1. 更优雅的方式: JavaScript 中顺序执行异步函数

    火于异步 1995年,当时最流行的浏览器--网景中开始运行 JavaScript (最初称为 LiveScript). 1996年,微软发布了 JScript 兼容 JavaScript.随着网景.微 ...

  2. 解决Powershell中不能运行脚本问题

    问题: powershell中不能执行脚本,提示‘because running scripts is disabled on this system’ 原因: powershell中默认的execu ...

  3. c#中@标志的作用 C#通过序列化实现深表复制 细说并发编程-TPL 大数据量下DataTable To List效率对比 【转载】C#工具类:实现文件操作File的工具类 异步多线程 Async .net 多线程 Thread ThreadPool Task .Net 反射学习

    c#中@标志的作用   参考微软官方文档-特殊字符@,地址 https://docs.microsoft.com/zh-cn/dotnet/csharp/language-reference/toke ...

  4. 在UI程序设计中使用BackgroundWorker进行多线程异步处

    WinForm的应用程序中如果执行了一个的非常冗长的处理操作,(比如文件检索,大运算量计算),在执行时就会锁定用户界面,虽然主活动窗口还在运行,但用户无法与程序交互,无法移动窗体或改变窗体大小,导致程 ...

  5. 在本地主机上powershell中连接远程主机执行vbs脚本,得到执行结果(2008版及以上)

    在桌面版的主机上远程管理服务器版主机,在本地powershell中连接远程主机执行vbs脚本,得到执行结果. 执行步骤: 1.将本地主机上的Hyper.vbs复制到远程连接主机上.例如,本地vbs脚本 ...

  6. PowerShell因为在此系统中禁止执行脚本解决方法

    PowerShell因为在此系统中禁止执行脚本解决方法   在Powershell直接脚本时会出现: 无法加载文件 ******.ps1,因为在此系统中禁止执行脚本.有关详细信息,请参阅 " ...

  7. Laravel框架中实现supervisor执行异步进程

    问题描述:在使用Laravel框架实现动态网页时,若有些操作计算量较大,为了不影响用户体验,往往需要使用异步方式去处理.这里使用supervisor和laravel自带的queues实现. Super ...

  8. angularjs中如何在异步请求执行完以后再执行其他函数?

    angularjs中如何在异步请求执行完以后再执行其他函数? 之前脑袋回路就是从上到下的执行js,直到有一次我的页面上已经显示了空才走到angularjs里的$http的成功回调函数里,然后才开始正视 ...

  9. Combine 框架,从0到1 —— 4.在 Combine 中执行异步代码

    本文首发于 Ficow Shen's Blog,原文地址: Combine 框架,从0到1 -- 4.在 Combine 中执行异步代码. 内容概览 前言 用 Future 取代回调闭包 用输出类型( ...

随机推荐

  1. Chrome插件触发web页面的事件

    Chrome插件中不能直接调用Web页面的元素js,原因是chrome插件的机制http://stackoverflow.com/questions/17819344/triggering-a-cli ...

  2. DDD - 概述 - 聚合 - 限界上下文 (四)

    最重要的一句话 DDD的所有有相关理论中,只有一句是至关重要的,但是也是最容易被忽略和最难做到的,抛弃传统的设计方式(思路)的思想,这句话起了决定性的作用,但是99%的人都忽略了或者在开始无法正视或理 ...

  3. Sublime插件:Terminal

    这几天在window环境下用gulp构建前端工程,切来切去浪费了不少时间(右键sublime菜单打开文件所在目录,然后去项目根目录,右键打开cmder).这点webstorm自带的Terminal真的 ...

  4. 结对开发项目--石家庄地铁web版

    一.功能要求 1.数据库设计:将石家庄地铁线路图的各个线路,各个站点,换乘信息等用数据库的形式保存起来,应该保存的信息有 {线路号,线路的各个站名,车站的换乘信息}. 2.站点查询:用户可以输入任一一 ...

  5. 理解WindowManagerService

    --摘自<Android进阶解密> *WMS的职责* 1)窗口管理 WMS负责窗口的启动.添加和删除,另外窗口的大小和层级也是由WMS进行管理的 2)窗口动画 WMS的动画子系统Windo ...

  6. 20162329张旭升 2018-2019-2《网络对抗技术》第1周 Kali的安装

    目录 Kali的安装 设置与主机的文件交互 Kali的安装 1.创建虚拟机 2.选择安装语言 3.设置网络 4.设置分区 5.安装完成 设置与主机的文件交互 首先在vmware workstation ...

  7. BZOJ5335 : [TJOI2018]智力竞赛

    二分答案,转化成求最少的路径,覆盖住所有权值$\leq mid$的点. 建立二分图,若$i$的后继为$j$,则连边$i\rightarrow j$,求出最大匹配,则点数减去最大匹配数即为最少需要的路径 ...

  8. 安装xlwt和xlrd

    因为想用python实现读写excel.百度了下,要安装xlwt和xlrd,网上各种方法,很多都不便利.最后利用pip安装很方便. 第一步:浏览器检索“xlwt安装”,点击第一个网页(百度) 即出现如 ...

  9. CTF最简单的Web题

    http://www.shiyanbar.com/ctf/1810 天网管理系统天网你敢来挑战嘛格式:ctf{ }解题链接: http://ctf5.shiyanbar.com/10/web1 查看源 ...

  10. jdk12+tomcat9 配置

    jdk12 没有了jre的配置   直接配置path就可以了 tomcat常规配置,只是启动的时候麻烦一点   需要重新写入jdk jre jvm 到server.bat文件 参考文章: https: ...