Grilling Your CPU (Responsibly): PowerShell Multithreading
You know that feeling when you run a PowerShell script and watch it crawl through a list of servers one… by… one… while you slowly question your career choices?
Yeah, same.
Turns out, PowerShell doesn’t have to move at glacial speed. You can spin up multiple threads and make your system actually sweat a little.
Here’s a little function I use for multi-threading: Invoke-MultiThreading.
It uses runspaces (PowerShell’s version of lightweight threads) to execute many tasks at once. The entire wrapper is on my github, but I’ll explain some of the snippets here.
The script itself doesn’t actually perform any tasks. It needs to be passed a list of machines as well as a script which contains the ‘work’ that needs to be done for each machine. There’s also parameters to set the max number of threads, a sleep timer, and the max result time which is the max amount of time a single thread will wait until it times out.
function Invoke-MultiThreading {
Param($Command = $(Read-Host "Enter the script file"),
[Parameter(ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)]$ObjectList,
$InputParam = $Null,
$MaxThreads = 20,
$SleepTimer = 200,
$MaxResultTime = 120,
[HashTable]$AddParam = @{},
[Array]$AddSwitch = @()
)
Begin{...)
I have used this method to drastically increase the runtime of some of my scripts. For a simple example we can setup a ping sweep for a given network.
Let’s say you want to check 50 hosts in your subnet. Normally, you’d do something like this:
$computers = 1..50 | ForEach-Object { "192.168.1.$_" }
foreach ($c in $computers) {
Test-Connection -ComputerName $c -Count 1 -Quiet
}
That’s fine… if you also enjoy watching paint dry.
Now, here’s the multi-threaded version using the wrapper:
Ping-Host.ps1
param([string]$ComputerName)
$ping = Test-Connection -ComputerName $ComputerName -Count 1 -Quiet -ErrorAction SilentlyContinue
if ($ping) {
Write-host -ForegroundColor Green "$ComputerName is online"
} else {
Write-host -ForegroundColor Red "$ComputerName is offline"
}
Run it like this:
$computers = 1..50 | ForEach-Object { "192.168.1.$_" }
$computers | Invoke-MultiThreading -Command "Ping-Host.ps1" -InputParam ComputerName -MaxThreads 10
That’s it — ten hosts pinged at once, which of course isn’t very exciting. But this can be used for so many other things, such as restarting services, pulling information, changing configurations or anything your heart desires.