介绍
在 PowerShell 中进行多任务处理(Multithreading 或 Parallel Processing)主要目的是提高脚本的执行效率和性能。对于需要处理大量数据或执行多个独立任务的脚本来说尤其有用。
-
提高性能: 多任务处理允许脚本同时执行多个任务,从而加快整体执行速度。对于需要处理大型数据集或执行耗时的操作时尤为重要。
-
充分利用多核处理器: 现代计算机配备了多核处理器。通过多任务处理,充分发挥这些多核处理器的性能,提高整体计算能力。
-
并行执行独立任务: 在某些情况下,脚本需要执行多个相互独立的任务。多任务处理使得这些任务同时进行,而不是依次执行,提高脚本的效率。
-
提高响应性: 脚本需要响应用户输入或事件,多任务处理可以确保主线程(通常是用户界面线程)保持响应,而后台线程处理其他任务。
-
资源分配: 多任务处理允许更灵活地分配系统资源,例如内存、CPU 时间等,以满足不同任务的需求。
Runspace
在 PowerShell 中,使用 PowerShell Jobs、Runspace 等技术来实现多任务处理。 本文使用 Runspace,Runspace 是 PowerShell 中用于实现多任务处理的一种机制,其优势主要体现在以下几个方面:
-
轻量级: Runspace 是一种轻量级的线程池技术,相较于创建完整的 PowerShell 进程或 Runspace 进程,资源开销较小。在一个脚本中可以创建多个 Runspace,以执行并发任务而不过度消耗系统资源。
-
资源隔离: 每个 Runspace 拥有独立的 PowerShell 运行环境和内存空间,意味着一个 Runspace 的异常不会影响到其他 Runspace,提供了更好的资源隔离。
-
更好的性能: Runspace 可以同时执行多个任务,充分利用多核处理器和系统资源,从而提高整体脚本执行的性能。在需要处理大量数据或执行复杂任务时,Runspace 能够更高效地完成工作。
-
适用于并行处理: Runspace 是适用于并行处理的一种机制,可以同时执行多个任务,不是按顺序一个一个地执行。这对于需要并行处理独立任务的脚本非常有帮助。
-
异步执行: Runspace 可以用于实现异步执行任务,意味着脚本可以继续执行而无需等待所有任务完成。对于需要同时进行多个长时间运行的任务时尤为有用。
示例
# 开始时间
$StartTime = Get-Date
# 处理任务
$Worker = {
param($Filename)
Write-Host "Processing $filename"
Start-Sleep -Seconds 1 # Doing some work....
$Item = New-Item -Name $Filename
Write-Output $Item.FullName
}
# 最大并发
$MaxRunspaces = 10
try {
# 线程池
$RunspacePool = [runspacefactory]::CreateRunspacePool(1, $MaxRunspaces)
$RunspacePool.Open()
$Jobs = New-Object System.Collections.ArrayList
$Filenames = @("file1.txt", "file2.txt", "file3.txt", "file4.txt", "file5.txt", "file6.txt", "file7.txt", "file8.txt", "file9.txt", "file10.txt", "file11.txt")
foreach ($File in $Filenames) {
Write-Host "Creating runspace for $File"
$PowerShell = [powershell]::Create()
$PowerShell.RunspacePool = $RunspacePool
$PowerShell.AddScript($Worker).AddArgument($File) | Out-Null
$JobObj = New-Object -TypeName PSObject -Property @{
Runspace = $PowerShell.BeginInvoke()
PowerShell = $PowerShell
}
$Jobs.Add($JobObj) | Out-Null
}
while ($Jobs.Runspace.IsCompleted -contains $false) {
Write-Host (Get-date).Tostring() "Still running..."
Start-Sleep 1
}
}
finally {
# 始终关闭进程池
$RunspacePool.Close()
}
# 结束时间
$endTime = Get-Date
# 运行时间
Write-Host "The script runs for Total time (s):$(($endTime - $StartTime).TotalSeconds)"
# 任务对象信息
$Jobs.powershell.Streams
$Jobs.powershell.haderrors
逻辑介绍
代码逻辑的解释:
-
定义 Worker 脚本块:
$Worker
脚本块是一个用于处理文件的脚本块。接受一个文件名参数,输出处理后的结果。
-
设置并发 Runspaces 数量:
$MaxRunspaces
变量定义最大 Runspaces 数量,即同时处理的最大任务数。
-
创建 Runspace 池:
- 使用
[runspacefactory]::CreateRunspacePool
创建 Runspace 池,设置最小和最大 Runspaces 数量,并打开池。
- 使用
-
创建并发任务:
- 使用
foreach
循环遍历文件名数组$Filenames
。 - 每个文件名创建一个 PowerShell 对象
$PowerShell
。 - 将
$Worker
脚本块添加到 PowerShell 中,并通过AddArgument
传递文件名参数。 - 使用
BeginInvoke
异步启动 PowerShell,返回一个 Runspace 对象。 - 创建一个包含 Runspace 和 PowerShell 对象的自定义对象
$JobObj
,并将其添加到$Jobs
数组中。
- 使用
-
等待任务完成:
- 使用
while
循环检查所有任务是否完成。循环条件是检查$Jobs.Runspace.IsCompleted
数组,确保所有任务都完成。 - 在循环中使用
Start-Sleep
暂停一秒钟,以避免过于频繁的检查。
- 使用
-
关闭 Runspace 池:
- 在
finally
块中关闭 Runspace 池,确保资源的正确释放。
- 在
-
输出结果和错误信息:
- 输出所有任务的 Streams(输出、错误等)。
- 输出是否有错误发生(
$Jobs.powershell.haderrors
)。
这段代码通过并发处理多个文件,每个文件在一个独立的 Runspace 中执行,最终输出各任务的结果和错误信息。并发处理可以提高处理大量文件时的效率。
Ending
::: hljs-center
喜欢的话,请收藏+关注~ 万一有趣的事还在后头呢~