约 2348 字

约 12 分钟

#运行空间与并发

#简介

运行空间(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。

#注意事项

  • 并发写同一文件或调用同一外部服务时,需要自行加锁或限流。

#推荐阅读

更新: 2026/11/2

作者: planc

创建: 2026/11/2