In the previous parts, you learned two concepts for speeding up code:
- Parallel loops: Execute the same script block multiple times. -ThrottleLimit sets the maximum number of threads that can run simultaneously.
- Jobs: Execute a single script block in the background. By starting multiple jobs, you can shift several tasks to the background. Jobs do not have a -ThrottleLimit because they run independently of each other.
To conclude this introductory section on parallelization, let’s combine the benefits of parallel loops and jobs: why not execute different script blocks within a parallel loop?
Executing different script blocks in a parallel loop combines the features of parallel loops and jobs. Unlike jobs, this approach has built-in throttling, allowing you to actively control how many threads are used to execute the list of script blocks.
PowerShell 7
Here is a script that can handle any number of tasks (script blocks). Note that a script block can span multiple lines and include hundreds of commands.
# list of tasks you need to do
$jobArray =
{ Get-Process },
{ Get-Service },
{ Get-Date }
# number of threads you want to invest
$ThrottleLimit = 3
# feed the script blocks in...
$result = $jobArray |
# wrap each script block in an object with some metadata
# so we later know where the result belongs
ForEach-Object -Begin { $id = 0 } -Process {
[PSCustomObject]@{
Id = $id++
Code = $_
Result = $null
}
} |
# execute script block in parallel
ForEach-Object -Parallel {
# execute code and save result, then return the wrapper object
$_.Result = & $_.Code
$_
} -ThrottleLimit $ThrottleLimit |
# group all results by their ID so they can be associated to the
# original command
Group-Object -Property Id -AsHashTable
# getting to the results of the first script block
$result[0].Result
These script blocks are executed by ForEach-Object -Parallel. However, there is one caveat: when you run script blocks in parallel, results may not arrive sequentially.
That’s why a separate ForEach-Object wraps each script block in an object with metadata: it receives a numeric ID and the original script block, so we can later easily associate results with the corresponding task.
At the end, Group-Object adds an index to the results using the id property. This makes it simple to access the results for a particular script block: the results for the first script block are always in $result[0], the results for the second script block in $result[1], and so on.
Windows PowerShell
The script for Windows PowerShell looks almost identical. It simply uses Invoke-Parallel instead of ForEach-Object -Parallel and requires that you have installed the PSParallel module from the PowerShell Gallery.
# list of tasks you need to do
$jobArray =
{ Get-Process },
{ Get-Service },
{ Get-Date }
# number of threads you want to invest
$ThrottleLimit = 3
# feed the script blocks in...
$result = $jobArray |
# wrap each script block in an object with some metadata
# so we later know where the result belongs
Foreach-Object -begin { $id=0 } -process {
[PSCustomObject]@{
Id = $id++
Code = $_
Result = $null
}
} |
# execute script block in parallel
Invoke-Parallel {
# execute code and save result, then return the wrapper object
$_.Result = & $_.Code
$_
} -ThrottleLimit $ThrottleLimit |
# group all results by their ID so they can be associated to the
# original command
Group-Object -Property Id -AsHashTable
# getting to the results of the first script block
$result[0].Result
Related links
- ScriptRunner ActionPacks will help you automate tasks
- Try out ScriptRunner here
- ScriptRunner: Book a demo with our product experts

