#运行空间与并发
#简介
运行空间(Runspace)是 PowerShell 引擎的一次执行环境,包含独立的会话状态、变量、函数和模块。每个 PowerShell 实例都需要绑定一个运行空间。需要在同一进程中并发执行多个 PowerShell 任务时,可以使用运行空间、线程作业或 ForEach-Object -Parallel。
#创建运行空间
下面的示例创建一个运行空间,同步执行脚本并取回结果:
$runspace = [runspacefactory]::CreateRunspace()
$runspace.Open()
$ps = [powershell]::Create()
$ps.Runspace = $runspace
$ps.AddScript('param($n) $n * $n').AddArgument(12) | Out-Null
$result = $ps.Invoke()
$result
$ps.Dispose()
$runspace.Close()
$runspace.Dispose()
结果为 144。
创建和销毁运行空间有一定开销。需要反复执行任务时,通常使用运行空间池:[runspacefactory]::CreateRunspacePool(1, 4),然后把实例的 RunspacePool 属性设置为该池。
#PowerShell 实例
AddScript、AddCommand、AddParameter 用于构造要执行的命令。Invoke 同步执行,BeginInvoke 与 EndInvoke 支持异步执行:
$ps = [powershell]::Create()
$ps.AddScript('Start-Sleep -Milliseconds 200; 42') | Out-Null
$handle = $ps.BeginInvoke()
# 这里可以继续执行其他工作
$result = $ps.EndInvoke($handle)
$result
$ps.Dispose()
结果为 42。
异步执行适合需要并行调用多个独立任务的场景。实例和运行空间使用后应及时释放。
#ForEach-Object -Parallel
PowerShell 7.0 起,ForEach-Object 支持 -Parallel,每个并行分支运行在独立运行空间中:
$prefix = 'item'
1..4 | ForEach-Object -Parallel {
Start-Sleep -Milliseconds 100
"$($using:prefix)-$_"
} -ThrottleLimit 2
-ThrottleLimit 控制同时运行的分支数量,默认值为 5。外部变量必须通过 $using: 访问,不能直接读取。并行输出的顺序不保证与输入顺序一致。
#Start-ThreadJob
Start-ThreadJob 在同一个进程中创建线程作业,启动速度比进程作业快,适合短时间任务:
$job = Start-ThreadJob -ScriptBlock {
param($number)
$number * $number
} -ArgumentList 9
$job | Receive-Job -Wait -AutoRemoveJob
结果为 81。
ThreadJob 模块随 PowerShell 6.0 及以上版本提供。需要跨进程隔离或更长时间运行时,可考虑 Start-Job。
#线程安全注意事项
- 线程作业和运行空间共享同一进程内存,不要并发修改同一个普通集合或文件。
- 优先让任务通过输出返回结果,由主线程汇总。
- 必须共享状态时,使用线程安全的集合或同步机制。
$using:传入的是调用时的值;对象仍可能被多个线程引用,不要随意修改。- 并行任务中的错误需要通过作业结果或
Receive-Job检查,不能依赖主线程的$Error。
#注意事项
- 并发写同一文件或调用同一外部服务时,需要自行加锁或限流。