是否有一种更简单的方法可以并行运行命令,同时在 Windows PowerShell 中保持高效?

2024-05-20

此自我回答旨在为那些受困于 Windows PowerShell 并由于公司政策等原因而无法安装模块的用户提供一种简单且高效的并行替代方案。

在 Windows PowerShell 中,built-in可用的替代方案local并行调用是Start-Job https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/start-job?view=powershell-7.2 and workflow https://learn.microsoft.com/en-us/powershell/module/psworkflow/about/about_workflows?view=powershell-5.1,两者都已知非常慢、效率低,其中之一(workflow)甚至不建议使用并且不再可用新版本的 PowerShell https://github.com/PowerShell/PowerShell.

另一种选择是依靠PowerShell SDK https://learn.microsoft.com/en-us/powershell/scripting/developer/windows-powershell?view=powershell-7.2并使用以下代码编写我们自己的并行逻辑System.Management.Automation.Runspaces命名空间 https://learn.microsoft.com/en-us/dotnet/api/system.management.automation.runspaces?view=powershellsdk-7.0.0必须提供。这绝对是最有效的方法,也是ForEach-Object -Parallel https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/foreach-object?view=powershell-7.2#-parallel(在 PowerShell Core 中)以及Start-ThreadJob https://learn.microsoft.com/en-us/powershell/module/threadjob/start-threadjob?view=powershell-7.2(预安装在 PowerShell Core 中,并可通过 Windows PowerShell 使用PowerShell 画廊 https://www.powershellgallery.com/packages/ThreadJob/2.0.0)在幕后使用。

一个简单的例子:

$throttlelimit = 3

$pool = [runspacefactory]::CreateRunspacePool(1, $throttlelimit)
$pool.Open()

$tasks = 0..10 | ForEach-Object {
    $ps = [powershell]::Create().AddScript({
        'hello world from {0}' -f [runspace]::DefaultRunspace.InstanceId
        Start-Sleep 3
    })
    $ps.RunspacePool = $pool

    @{ Instance = $ps; AsyncResult = $ps.BeginInvoke() }
}

$tasks | ForEach-Object {
    $_.Instance.EndInvoke($_.AsyncResult)
}

$tasks.Instance, $pool | ForEach-Object Dispose

这很棒,但当代码更加复杂时,就会变得乏味并且常常变得复杂,从而带来很多问题。

有更简单的方法吗?


NOTE

此功能的进一步更新将发布到GitHub 仓库 https://github.com/santisq/PSParallelPipeline以及PowerShell 画廊 https://www.powershellgallery.com/packages/PSParallelPipeline/。这个答案中的代码将不再维护.

可以找到该功能的文档以及使用示例here https://github.com/santisq/PSParallelPipeline/blob/main/docs/en-US/Invoke-Parallel.md。请注意,模块版本不再有-ThreadOptions参数及实现-UseNewRunspace and -TimeoutSeconds参数,但其用法应该相同。

我们非常欢迎贡献,如果您想贡献,请分叉存储库并提交包含更改的拉取请求。


由于这是一个可能令人困惑并且经常给网站带来问题的主题,因此我决定创建这个函数来简化这项繁琐的任务并帮助那些陷入 Windows PowerShell 困境的人。目的是让它尽可能简单和友好,它也应该是一个可以复制粘贴到我们的程序中的功能$PROFILE https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_automatic_variables?view=powershell-7.2#profile可以在需要时重复使用,并且不需要安装模块(如问题中所述)。

这个功能受到了 RamblingCookieMonster 的很大启发Invoke-Parallel https://github.com/RamblingCookieMonster/Invoke-Parallel和 Boe Prox 的PoshRSJob https://github.com/proxb/PoshRSJob并且只是对那些进行了一些改进的简化版本。

定义

using namespace System.Collections
using namespace System.Collections.Generic
using namespace System.Management.Automation
using namespace System.Management.Automation.Language
using namespace System.Management.Automation.Runspaces
using namespace System.Threading
using namespace System.Text

# The function must run in the scope of a Module.
# `New-Module` must be used for portability. Otherwise store the
# function in a `.psm1` and import it via `Import-Module`.
New-Module PSParallelPipeline -ScriptBlock {
    class CommandCompleter : IArgumentCompleter {
        [IEnumerable[CompletionResult]] CompleteArgument(
            [string] $commandName,
            [string] $parameterName,
            [string] $wordToComplete,
            [CommandAst] $commandAst,
            [IDictionary] $fakeBoundParameters) {

            return [CompletionCompleters]::CompleteCommand(
                $wordToComplete,
                [NullString]::Value,
                [CommandTypes]::Function)
        }
    }

    function Invoke-Parallel {
        [CmdletBinding(PositionalBinding = $false)]
        [Alias('parallel')]
        param(
            [Parameter(Mandatory, ValueFromPipeline)]
            [object] $InputObject,

            [Parameter(Mandatory, Position = 0)]
            [scriptblock] $ScriptBlock,

            [Parameter()]
            [ValidateRange(1, 63)]
            [int] $ThrottleLimit = 5,

            [Parameter()]
            [hashtable] $Variables,

            [Parameter()]
            [ValidateNotNullOrEmpty()]
            [ArgumentCompleter([CommandCompleter])]
            [string[]] $Functions,

            [Parameter()]
            [ValidateSet('ReuseThread', 'UseNewThread')]
            [PSThreadOptions] $ThreadOptions = [PSThreadOptions]::ReuseThread
        )

        begin {
            try {
                $iss = [initialsessionstate]::CreateDefault2()

                foreach ($key in $Variables.PSBase.Keys) {
                    if ($Variables[$key] -is [scriptblock]) {
                        $PSCmdlet.ThrowTerminatingError([ErrorRecord]::new(
                            [PSArgumentException]::new('Passed-in script block variables are not supported.'),
                            'VariableCannotBeScriptBlock',
                            [ErrorCategory]::InvalidType,
                            $Variables[$key]))
                    }

                    $iss.Variables.Add([SessionStateVariableEntry]::new($key, $Variables[$key], ''))
                }

                foreach ($function in $Functions) {
                    $def = (Get-Command $function).Definition
                    $iss.Commands.Add([SessionStateFunctionEntry]::new($function, $def))
                }

                $usingParams = @{}

                foreach ($usingstatement in $ScriptBlock.Ast.FindAll({ $args[0] -is [UsingExpressionAst] }, $true)) {
                    $varText = $usingstatement.Extent.Text
                    $varPath = $usingstatement.SubExpression.VariablePath.UserPath

                    # Thanks to mklement0 for catching up a bug here.
                    # https://github.com/mklement0
                    $key = [Convert]::ToBase64String([Encoding]::Unicode.GetBytes($varText.ToLowerInvariant()))
                    if (-not $usingParams.ContainsKey($key)) {
                        $value = $PSCmdlet.SessionState.PSVariable.GetValue($varPath)

                        if ($value -is [scriptblock]) {
                            $PSCmdlet.ThrowTerminatingError([ErrorRecord]::new(
                                [PSArgumentException]::new('Passed-in script block variables are not supported.'),
                                'VariableCannotBeScriptBlock',
                                [ErrorCategory]::InvalidType,
                                $value))
                        }

                        $usingParams.Add($key, $value)
                    }
                }

                $pool = [runspacefactory]::CreateRunspacePool(1, $ThrottleLimit, $iss, $Host)
                $tasks = [List[hashtable]]::new()
                $pool.ThreadOptions = $ThreadOptions
                $pool.Open()
            }
            catch {
                $PSCmdlet.ThrowTerminatingError($_)
            }
        }
        process {
            try {
                # Thanks to Patrick Meinecke for his help here.
                # https://github.com/SeeminglyScience/
                $ps = [powershell]::Create().
                    AddScript({ $args[0].InvokeWithContext($null, [psvariable]::new('_', $args[1])) }).
                    AddArgument($ScriptBlock.Ast.GetScriptBlock()).
                    AddArgument($InputObject)

                # This is how `Start-Job` does it's magic.
                # Thanks to Jordan Borean for his help here.
                # https://github.com/jborean93
                if ($usingParams.Count) {
                    $null = $ps.AddParameters(@{ '--%' = $usingParams })
                }

                $ps.RunspacePool = $pool

                $tasks.Add(@{
                    Instance    = $ps
                    AsyncResult = $ps.BeginInvoke()
                })
            }
            catch {
                $PSCmdlet.WriteError($_)
            }
        }
        end {
            try {
                while ($tasks.Count) {
                    $id = [WaitHandle]::WaitAny($tasks.AsyncResult.AsyncWaitHandle, 200)

                    if ($id -eq [WaitHandle]::WaitTimeout) {
                        continue
                    }

                    $task = $tasks[$id]
                    $task.Instance.EndInvoke($task.AsyncResult)

                    foreach ($err in $task.Instance.Streams.Error) {
                        $PSCmdlet.WriteError($err)
                    }

                    $tasks.RemoveAt($id)
                }
            }
            catch {
                $PSCmdlet.WriteError($_)
            }
            finally {
                foreach ($task in $tasks.Instance) {
                    if ($task -is [IDisposable]) {
                        $task.Dispose()
                    }
                }

                if ($pool -is [IDisposable]) {
                    $pool.Dispose()
                }
            }
        }
    }
} -Function Invoke-Parallel | Import-Module -Force
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

是否有一种更简单的方法可以并行运行命令,同时在 Windows PowerShell 中保持高效? 的相关文章

  • Java 空值检查

    我有一个thread1 if object null object play 和另一个thread2可以写null into object随时参考 我将同时运行这些线程 我知道thread2可以重写object后参考null检查并会抛出Nu
  • 提升::亚洲。消息是在哪个线程中发送的?

    我试图了解内部工作boost asio图书馆 这真的很棒 我编写了一个简单的客户端 用于向服务器发送一条消息 问题是 它真正在哪个线程中发送消息 自从我使用async write 方法 调用后立即返回并且不发送任何内容 我已经评论了io s
  • 在c99中使用__thread

    我想使用 thread 存储类定义一些特定于线程的变量 但三个问题让我犹豫不决 它真的是c99的标准吗 或者更重要的是 编译器支持有多好 变量会在每个线程中初始化吗 非多线程程序是否将它们视为普通的旧全局变量 回答您的具体问题 不 它不是
  • 检测您何时进入/退出 Xamarin.iOS 中的主线程

    Xamarin MonoTouch 有没有办法检测主线程中是否正在调用代码 我正在寻找类似于Java的东西EventQueue isEventDispatchThread 我发现 Swing 编程很方便assert时不时 或有时assert
  • Java 线程 JavaDoc

    我编写了一个只能在特定线程上调用的方法 是否应该将标准注释或注释添加到方法的 javadoc 中来表示这一点 不知道有任何这样的标准注释 Java 并发实践 http www javaconcurrencyinpractice com 在第
  • PowerShell(2.0,32 位)无法加载 TFS 2010 管理单元...除非可以

    我有一个与 Team Foundation Server 交互的 PowerShell 脚本 当我在 PowerShell 控制台中运行它时 它运行得很好 这对于测试它来说很好 但我想通过双击它或批处理文件或其他东西来运行它 我什至选择右键
  • Eclipse PTP:在本地计算机上运行并行(MPI)应用程序?

    必须如何配置 eclipse PTP 才能在本地计算机上使用 OpenMPI 运行 MPI 应用程序 使用 添加资源管理器 我可以选择 OpenMPI 并在 连接名称 中切换到本地主机 但仍然要求我提供一些用户名和密码 这是正确的方法吗 D
  • 使用变量作为启动进程的文件路径参数

    我想运行一个 exe 它可能位于多个位置 runpath servicepackfolder SQLServer2008SP1 KB968369 IA64 ENU exe Start Process FilePath runpath arg
  • 为什么我的代码在编译用于分析 (-pg) 时在多线程下运行比在单线程下运行慢?

    我正在写一个光线追踪器 最近 我在程序中添加了线程 以利用 i5 四核上的附加内核 奇怪的是 应用程序的调试版本现在运行速度变慢 但优化后的构建运行速度比添加线程之前更快 我将 g pg 标志传递给 gcc 以进行调试构建 并将 O3 标志
  • 如何获取管道对象的数量?我不想累积管道来缓冲

    假设我有一些 powershell 代码 function count pipe CmdletBinding param Parameter ValueFromPipeline true object inputObject process
  • C# 线程和队列

    这不是关于我可以或应该使用的不同方法来以最佳方式利用队列 而是关于我所看到的对我来说毫无意义的事情 void Runner member variable queue Queue Synchronized new Queue while t
  • Haskell 五个独特的 Wordle 单词

    为了好玩 我正在尝试解决 Matt Parker 在他的 Haskell 频道 Standup Maths in Haskell 频道的链接视频中谈到的与 Wordle 相关的问题 基本上 找到 5 个没有任何共同字母的 5 个字母单词 因
  • 使用 volatile bool 强制另一个线程等待是否安全? (C++)

    我读到的有关 volatile 的所有内容都说它永远不安全 但我仍然倾向于尝试它 而且我还没有看到这种特定场景被宣布为不安全 我有一个单独的线程来渲染场景 从主模拟线程中提取数据 这没有同步 并且工作正常 问题是 当程序退出时 渲染器需要停
  • shell脚本中是否有互斥/信号量机制?

    我正在 shell 脚本中寻找互斥 信号量 并发机制 考虑以下情况 除非 a 用户不关闭共享文件 否则 b 用户应该无法打开 更新它 我只是想知道如何在 shell 脚本中实现互斥量 信号量 临界区等 在 shell 脚本中实现锁定机制 文
  • C++ 中的多个异步调用

    我想多次调用异步方法 一个简化的示例如下所示 size t counter std string s return s size void stringCountAccumulator std vector
  • 无需停止程序即可输入

    我正在尝试制作一个倒计时器来打印剩余时间 当您输入某些内容时 它会打印您输入的内容 我的问题是我不想等待输入 只是继续运行计时器 我的错误代码 timer 100 while True print timer timer 1 if inpu
  • PHP 多个 Ajax 请求:第一个请求阻止第二个请求

    我在一页上有 2 个 ajax 请求 我运行了第一个请求并单独启动了第二个请求 但第二个在第一个运行后停止工作 第一次结束后继续 第一个请求需要很长时间 大约 30 60 秒 此时我需要第二个请求来显示日志第一个请求发生的情况 我尝试使用
  • 尝试写一个无锁的单链表,麻烦去除

    我正在尝试编写一个无锁单链表 最终一致性不是问题 有人遍历可能包含不正确项目的列表 我认为我正确添加了项目 循环和Interlocked CompareExchange 但我不知道如何删除节点 列表中的任何位置 因为我必须获取前一个项目并设
  • 如何在 PowerShell 中远程执行 ELEVATED 远程脚本

    我有两台服务器 serverA Windows 2003 服务器 serverB Windows 7的 ServerA包含一个带有批处理文件 deploy bat 的文件夹 需要从提升的 powershell 提示符执行该批处理文件 在Se
  • Powershell 中的“$”是什么?

    是什么意思 在 Powershell 中 Edit TechNet 答案 http technet microsoft com en us library hh847768 aspx同义反复 没有解释 成功 或 失败 的含义 包含上次操作的

随机推荐

  • 如何挤出平面 2D 网格并赋予其深度

    我有一组共面 连接的三角形 即二维网格 现在我需要将其在 z 轴上挤出几个单位 网格由一组顶点定义 渲染器通过与三角形数组匹配来理解这些顶点 网格示例 顶点 0 0 0 10 0 0 10 10 0 0 10 0 所以这里我们有一个二维正方
  • 如何将函数内的捕获错误传递给父级

    我有这几行代码示例 想知道下面的逻辑到底如何 try var response child console log why here catch err console log should show this err function c
  • 为什么这个 SKPhysicsJointPin 不能将这 2 个精灵保持在一起?

    我显然不太了解 SKPhysicsJoint 但是除了 Apple 文档之外 网上的信息还很少 下面的代码有什么问题 我认为应该保持头部和颈部永久连接 我的意图是它们就像两张带有大头针的纸 这样它们可以旋转一点 但不仅仅是完全分开 当我运行
  • 变量在嵌套 lambda 中生存时间不够长的借用检查错误

    我在嵌套 lambda 中遇到错误 let rows vec vec 3 6 2 8 9 0 vec 0 0 1 4 5 1 let pair sums rows iter flat map row 0 row len map i row
  • phpActiveRecord 日期时间格式不正确

    当尝试使用 phpActiveRecord 在表中创建记录时 出现以下错误 Invalid datetime format 1292 Incorrect datetime value 2013 06 20 11 59 08 PDT for
  • 批处理文件中是否存在“Power to”功能? (指数)

    Problem 有没有办法将变量 乘以 数字或其他变量的批处理文件 有这个功能吗 Python 中的一个示例是您可以使用 为 到 的力量 EDIT 您可以在批处理文件中进行数学运算 http en wikipedia org wiki Ba
  • JavaScript onresize 事件多次触发

    我在尝试仅在触发 onresize 事件时运行一次函数时遇到一些麻烦 我已经看过这个问题DOM onresize 事件 https stackoverflow com questions 1500312 javascript onresiz
  • 为什么 ISNUMERIC('.') 返回 1?

    最近我在 SQL Server 中使用 ISNUMERIC 时遇到了一个问题 导致找到了这段代码 SELECT ISNUMERIC 这会返回 1 如 true 所示 难道不应该像 false 一样返回 0 吗 See Numeric 损坏了
  • 获取 2 个数据集 c# 中的差异

    我正在编写一个简短的算法 它必须比较两个数据集 以便可以进一步处理两者之间的差异 我尝试通过合并这两个数据集并将结果更改放入新的数据集来实现此目标 我的方法如下所示 private DataSet ComputateDiff DataSet
  • 直接使用从密钥库加载的 SecretKey 时,密钥用户未经过身份验证

    我正在尝试使用 Cipher 和在 KeyStore 中加载的 SecretKey 来加密数据 但总是收到此错误 导致 android security KeyStoreException 关键用户未经过身份验证 我尝试自己创建 Secre
  • BRISK 特征检测器检测零个关键点

    下面显示的 Brisk 探测器没有给我任何关键点 有人可以提出一个问题吗 我将尝试用一些代码解释我在下面所做的事情 include opencv2 features2d features2d hpp using namespace cv u
  • 如何一步步遍历目录树?

    我发现了很多关于遍历目录树的示例 但我需要一些不同的东西 我需要一个带有某种方法的类 每次调用都会从目录返回一个文件 并逐渐遍历目录树 请问我该怎么做 我正在使用函数 FindFirstFile FindNextFile 和 FindClo
  • Ubuntu 上的 Python 2.7

    我是 Python 新手 正在 Linux 机器 Ubuntu 10 10 上工作 它正在运行 python 2 6 但我想运行 2 7 因为它有我想使用的功能 有人敦促我不要安装 2 7 并将其设置为我的默认 python 我的问题是 如
  • 从字符串中获取数字

    我有一个字符串 例如 lorem 110 ipusm 我想获取 110 我已经尝试过这个 preg match all 0 9 string ret 但这正在返回 Array 0 gt 1 1 gt 1 2 gt 0 我想要这样的东西 Ar
  • ActionScript、NetStream.Play.Failed iOS AIR 移动设备

    我正在尝试以类似于 Tiberiu Ionu Stan http stackoverflow com questions 2036107 aac mp4 not working in actionscript 3s netstream 的方
  • 如何获取 UIWebView 中元素的位置?

    我在 iPad 程序中加载了 html 的 UIWebView 通过使用 webkit column width 我将 html 分为几列 padding 0px height 1024px webkit column gap 0px we
  • Youtube as3 API 似乎不再起作用

    我正在使用 as3 Youtube 官方 API 我需要在 swf 文件中加载 API 播放器 我已经在几个项目上完成了 一切都很好 但几个小时后 我的所有项目现在都坏了 这是崩溃的代码片段 Security allowDomain www
  • 无法在前端使用 JavaScript Fetch API 将文件上传到 FastAPI 后端

    我正在尝试弄清楚如何将图像发送到我的 API 并验证生成的token那是在header的请求 到目前为止 这就是我所处的位置 app post endreProfilbilde async def endreProfilbilde requ
  • 数据表日期范围过滤器

    如何添加日期范围过滤器 like From To 我开始进行常规搜索和分页等工作 但我不知道如何制作日期范围过滤器 我正在使用数据表 1 10 11 版本 My code var oTable function callFilesTable
  • 是否有一种更简单的方法可以并行运行命令,同时在 Windows PowerShell 中保持高效?

    此自我回答旨在为那些受困于 Windows PowerShell 并由于公司政策等原因而无法安装模块的用户提供一种简单且高效的并行替代方案 在 Windows PowerShell 中 built in可用的替代方案local并行调用是St