Parallelism and Jobs
Prerequisites: Script blocks, Scope
Slow, independent tasks — pinging servers, calling APIs, processing files — finish much faster when run at the same time.
ForEach-Object -Parallel (PowerShell 7)
$timer = [System.Diagnostics.Stopwatch]::StartNew()
$results = 1..6 | ForEach-Object -Parallel {
Start-Sleep -Milliseconds 500
"task $_ done"
} -ThrottleLimit 6
$timer.Stop()
$results | Sort-Object
$timer.Elapsed.TotalSeconds -lt 2
task 1 done
task 2 done
task 3 done
task 4 done
task 5 done
task 6 done
True
Six half-second tasks finished in well under the 3 seconds they'd take one after another.
- Each item runs in its own runspace (a separate PowerShell engine in the same process).
-ThrottleLimitcaps how many run at once (default 5).- Results arrive in completion order, not input order — sort if order matters.
$using: for outside variables
Parallel script blocks can't see your variables; prefix them with
$using::
$prefix = 'server'
1..3 | ForEach-Object -Parallel { "$using:prefix-$_" } | Sort-Object
server-1
server-2
server-3
Don't modify shared collections from parallel blocks — output results and
collect them instead. If you must share, use a thread-safe type like
[System.Collections.Concurrent.ConcurrentBag[object]].
Thread jobs and background jobs
Jobs run work in the background while your script (or console) carries on:
$job = Start-ThreadJob -ScriptBlock { param($n) 1..$n | Measure-Object -Sum | Select-Object -ExpandProperty Sum } -ArgumentList 100
'Doing other work...'
$job | Wait-Job | Receive-Job
$job.State
$job | Remove-Job
Doing other work...
5050
Completed
| Command | Runs in | Start-up cost |
|---|---|---|
ForEach-Object -Parallel |
runspaces | low |
Start-ThreadJob |
runspaces | low |
Start-Job |
a separate process | high — but isolated |
Invoke-Command -AsJob |
remote computers | — |
In PowerShell 7, you can also append & to a pipeline to run it as a job:
Get-ChildItem -Recurse C:\ &.
When not to parallelize
Short tasks are often slower in parallel, because starting runspaces
costs time. Parallelize waits (network, disk, sleep), not tiny
calculations — and measure with Measure-Command.