Skip to content

Instantly share code, notes, and snippets.

@bryanvine
Last active August 29, 2015 14:23
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bryanvine/bddcc9ba7f6697a3a113 to your computer and use it in GitHub Desktop.
Save bryanvine/bddcc9ba7f6697a3a113 to your computer and use it in GitHub Desktop.
Make any powershell script multithreaded with this powershell function wrapper (Invoke-Multithread)
#Requires -Version 3.0
Function Invoke-Multithread{
<#
.SYNOPSIS
Wrapper function that lets you divide and conquer a server list to multithread it's execution.
.DESCRIPTION
Similar behavior to Invoke-Command -Asjob which lets you remotely start scriptblocks on target servers,
this function starts local jobs that are to be targeted at remote servers.
Great for multithreaded push deployments or report generation
.PARAMETER ComputerName
Specifies one or more server names.
.PARAMETER jobheader
Optional prefix name for jobs, so you can use more than one Invoke-Multithread on the same machine
.PARAMETER ThrottleLimit
Limits the number of jobs started. If you have 1000 servers, the default limit of 25 will mean you'll get 25 jobs, each with 40 servers per job.
Don't see the limit too large as it will eat up more CPU and RAM which actually might cause things to run slower than executing sequentially.
.EXAMPLE
Invoke-Multithread -ComputerName (Get-Content .\myserverlist.txt) -ScriptBlock{<your script here, with -computername as a parameter>}
Starts up to 25 threads of your code for local execution and returns results back in an object array with computer names
.LINK
http://www.bryanvine.com/2015/06/powershell-script-invoke-multithread.html
.LINK
Start-Job
.LINK
Get-Job
.LINK
Receive-Job
.LINK
Invoke-Command
.NOTES
Author: Bryan Vine
Last updated: 6/23/2015
#>
[CmdletBinding()]
Param(
[Parameter(Mandatory=$true,valuefrompipeline=$false,Position=0)]
[alias("CN","MachineName","ServerName","Server")][ValidateNotNullOrEmpty()][string[]]
$ComputerName,
[Parameter(Mandatory=$true)][scriptblock]$ScriptBlock,
[string]$jobheader='Multi',
[int]$ThrottleLimit = 25
)
BEGIN{
$jobs = 1
$i = 0
#Clear out jobs from previous runs
Get-Job "$jobheader*"| Remove-Job -Force
}
PROCESS{
#Calculates number of computers per thread
$BatchSize = [math]::Round($ComputerName.count / $ThrottleLimit,0) + 1
#loop while there's still work to be done
while($jobs -and $i -lt $ComputerName.count){
#return jobs that are finished and parses output per computer name as nice objects
Get-Job "$jobheader*" | ?{$_.State -like "completed"}|%{
$servers = ($_ | select -ExpandProperty Command).replace($ScriptBlock.ToString(),"").replace("-ComputerName ('","").replace("').split(',')","").split(",")
$jobreturn = $_ | Receive-Job
0..($servers.count -1) | %{
$obj = New-Object PSobject
$obj | Add-Member NoteProperty ComputerName $servers[$_]
$obj | Add-Member NoteProperty Output $jobreturn[$_]
Write-Output $obj
}
}
if($jobs -lt $ThrottleLimit){
#spawn more jobs
#break up server list into batches to kick off multiple jobs
$batch = @()
1..$BatchSize | %{
$batch += $ComputerName[$i++] |?{$_ -notlike ''}
}
if($batch){
$batchstring = $batch -join ","
#start the job by appending the -computername property with the servernames to the scriptblock
Start-Job -Name "$jobheader-$i" -ScriptBlock ([scriptblock]::Create("$($ScriptBlock.ToString()) -ComputerName ('$batchstring').split(',')")) | Out-Null
}
}
else{
Start-Sleep -Seconds 1
}
#calculate the number of jobs currently running
$jobs = (Get-Job "$jobheader*" |?{$_.State -like "Running"})| Measure | select -ExpandProperty count
}
}
END{
#return all jobs after completed
Get-Job "$jobheader*" | Wait-Job | %{
$servers = ($_ | select -ExpandProperty Command).replace($ScriptBlock.ToString(),"").replace(" -ComputerName ('","").replace("').split(',')","").split(",")
$jobreturn = $_ | Receive-Job
0..($servers.count -1) | %{
$obj = New-Object PSobject
$obj | Add-Member NoteProperty ComputerName $servers[$_]
$obj | Add-Member NoteProperty Output $jobreturn[$_]
Write-Output $obj
}
}
#cleanup
Get-Job "$jobheader*"| Remove-Job
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment