如果我有一个脚本需要在多台计算机上运行,或者使用多个不同的参数,我怎样才能并行执行它,而不必产生产生新PSJobStart-Job
的开销?
例如,我想重新同步所有域成员的时间,如下所示:
$computers = Get-ADComputer -filter * |Select-Object -ExpandProperty dnsHostName
$creds = Get-Credential domain\user
foreach($computer in $computers)
{
$session = New-PSSession -ComputerName $computer -Credential $creds
Invoke-Command -Session $session -ScriptBlock { w32tm /resync /nowait /rediscover }
}
但我不想等待每个 PSSession 连接并调用命令。没有乔布斯,这怎么能并行完成?
更新 - 虽然这个答案解释了 PowerShell 运行空间的过程和机制,以及它们如何帮助您处理多线程非顺序工作负载,但 PowerShell 爱好者Warren 'Cookie Monster' F已经加倍努力,并将这些相同的概念整合到一个工具中称为 - 它执行我在下面描述的操作,并且他已经使用可选开关进行了扩展,用于记录和准备会话状态,包括导入的模块,非常酷的东西 - 我强烈建议您在构建自己的闪亮解决方案之前检查它!
Invoke-Parallel
使用并行运行空间执行:
减少不可避免的等待时间
在最初的特定情况下,被调用的可执行文件有一个
/nowait
选项可以防止在作业(在这种情况下,时间重新同步)自行完成时阻塞调用线程。从发行者的角度来看,这大大减少了整体执行时间,但连接到每台机器仍然是按顺序完成的。由于超时等待的累积,按顺序连接到数千个客户端可能需要很长时间,具体取决于由于某种原因或其他原因而无法访问的机器数量。
为了避免在单个或几个连续超时的情况下将所有后续连接排队,我们可以将连接和调用命令的作业分派到单独的 PowerShell 运行空间,并行执行。
什么是运行空间?
运行空间是您的powershell代码在其中执行的虚拟容器,并从 PowerShell 语句/命令的角度表示/保存环境。
从广义上讲,1 个运行空间 = 1 个执行线程,因此我们对 PowerShell 脚本“多线程”所需的只是运行空间的集合,这些运行空间随后可以并行执行。
与最初的问题一样,调用多个运行空间的命令的工作可以分解为:
运行空间池模板
PowerShell 有一个类型加速器
[RunspaceFactory]
,它可以帮助我们创建运行空间组件——让我们把它投入使用1. 创建一个 RunspacePool 并且
Open()
它:传递给 和 的两个参数
CreateRunspacePool()
是允许在任何给定时间执行的运行空间的最小和最大数量,给我们一个有效的1
最大并行度为 8。8
2. 创建一个 PowerShell 实例,附加一些可执行代码并将其分配给我们的 RunspacePool:
PowerShell 的实例与
powershell.exe
进程(实际上是主机应用程序)不同,它是一个内部运行时对象,表示要执行的 PowerShell 代码。我们可以使用[powershell]
类型加速器在 PowerShell 中创建一个新的 PowerShell 实例:3. 使用 APM 异步调用 PowerShell 实例:
使用 .NET 开发术语中众所周知的异步编程模型,我们可以将命令的调用拆分为一个
Begin
方法,为执行代码提供“绿灯”,以及一个End
收集结果的方法。由于在这种情况下我们对任何反馈都不感兴趣(无论如何我们都不等待输出w32tm
),我们可以通过简单地调用第一个方法来完成将其包装在 RunspacePool 中
使用上述技术,我们可以将创建新连接和调用远程命令的顺序迭代包装在并行执行流程中:
假设 CPU 有能力一次执行所有 8 个运行空间,我们应该能够看到执行时间大大减少,但由于使用了相当“高级”的方法,因此以脚本的可读性为代价。
确定最佳平行度:
我们可以轻松地创建一个 RunspacePool,它允许同时执行 100 个运行空间:
但归根结底,这一切都取决于我们的本地 CPU 可以处理多少个执行单元。换句话说,只要您的代码正在执行,允许比逻辑处理器更多的运行空间来分派代码执行是没有意义的。
多亏了 WMI,这个阈值很容易确定:
另一方面,如果您正在执行的代码由于网络延迟等外部因素而导致大量等待时间,您仍然可以从运行比逻辑处理器更多的同时运行空间中受益,因此您可能想要测试范围可能的最大运行空间以找到收支平衡:
除了这个讨论之外,还缺少一个收集器来存储从运行空间创建的数据,以及一个用于检查运行空间状态的变量,即它是否已完成。
查看PoshRSJob。它提供与本机 *-Job 函数相同/相似的功能,但使用的运行空间往往比标准 Powershell 作业更快且响应更快。
@mathias-r-jessen 有一个很好的答案,尽管我想补充一些细节。
最大线程数
理论上线程应该受到系统处理器数量的限制。但是,在测试AsyncTcpScan时,我通过为
MaxThreads
. 因此,为什么该模块具有-MaxThreads
输入参数。请记住,分配太多线程会影响性能。返回数据
从那里取回数据
ScriptBlock
很棘手。我已经更新了 OP 代码并将其集成到用于AsyncTcpScan的代码中。