Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- <#
- .SYNOPSIS
- A parallel ForEach that uses runspaces
- .PARAMETER ScriptBlock
- ScriptBlock to execute for each InputObject
- .PARAMETER ScriptFile
- Script file to execute for each InputObject
- .PARAMETER InputObject
- Object(s) to run script against in parallel
- .PARAMETER Throttle
- Maximum number of threads to run at one time. Default: 5
- .PARAMETER Timeout
- Stop each thread after this many minutes. Default: 0 (Do not timeout)
- WARNING: This parameter should be used as a failsafe only
- Set it for roughly the entire duration you expect for all threads to complete
- .PARAMETER SleepTimer
- When looping through open threads, wait this many milliseconds before looping again. Default: 200
- .PARAMETER ProgressId
- Identity of the progress bar to use for display. Default: 0
- .PARAMETER Activity
- String to pass to the progress bar for the -Activity parameter. No progress is displayed unless this is specified. Default: Blank and do not show progress.
- .EXAMPLE
- (1..50) | ForEach-Parallel -Throttle 4 -ScriptBlock { $this; Start-Sleep -Seconds (Get-Random -Minimum 0 -Maximum 5) } -Activity "One to fifty"
- Send the number 1 through 50 to the scriptblock. For each, display the number and then sleep for 0 to 5 seconds. Display a progress bar. Only execute 4 threads concurrently.
- .EXAMPLE
- Get-Content .\servers.txt | Foreach-Parallel -Throttle 20 -Timeout 10 -SleepTimer 200 -ScriptFile .\query.ps1 -Verbose
- Run query.ps1 against each computer in the servers.txt file. Run 20 concurrently, timeout a thread if it takes longer than 10 minutes to run, give verbose output.
- .FUNCTIONALITY
- PowerShell Language
- .NOTES
- Credit to Tome Tanasovski for the original ForEach-Parallel, from which this script has been modified.
- http://powertoe.wordpress.com/2012/05/03/foreach-parallel/
- Added:
- - Progress bar
- - Verbose output
- - Changed $_ to $this within the scriptblock to avoid pipeline contention
- #>
- [cmdletbinding()]
- PARAM (
- [Parameter(Mandatory=$false,position=0,ParameterSetName='ScriptBlock')]
- [scriptblock]$ScriptBlock,
- [Parameter(Mandatory=$false,ParameterSetName='ScriptFile')]
- [ValidateScript({test-path $_ -pathtype leaf})]
- $ScriptFile,
- [Parameter(Mandatory=$true,ValueFromPipeline=$true)]
- [PSObject]$InputObject,
- [int]$Throttle = 5,
- [double]$SleepTimer = 200,
- [double]$Timeout = 0,
- [int]$ProgressId = 0,
- [string]$Activity = 'Default'
- )
- BEGIN {
- $stopWatch = [Diagnostics.Stopwatch]::StartNew()
- if ($Activity -ne 'Default') { Write-Progress -Id $ProgressId -Activity $Activity -Status "Preparing" -PercentComplete 0 -CurrentOperation "Elapsed time: $($stopWatch.Elapsed.toString().subString(0,8))" }
- #Build the scriptblock depending on the parameter used
- switch ($PSCmdlet.ParameterSetName) {
- 'ScriptBlock' {$ScriptBlock = $ExecutionContext.InvokeCommand.NewScriptBlock("param(`$this)`r`n" + $Scriptblock.ToString())}
- 'ScriptFile' {$scriptblock = [scriptblock]::Create($(get-content $ScriptFile | out-string))}
- Default {Write-Error ("Must provide ScriptBlock or ScriptFile"); Return}
- }
- #Define the initial sessionstate, create the runspacepool
- Write-Verbose "Creating runspace pool with $Throttle threads"
- $sessionState = [system.management.automation.runspaces.initialsessionstate]::CreateDefault()
- $pool = [runspacefactory]::CreateRunspacePool(1, $Throttle, $sessionState, $host)
- $pool.open()
- #array to hold details on each thread
- $threads = @()
- #If inputObject is bound get a total count and set bound to true
- $bound = $false
- if( $PSBoundParameters.ContainsKey("inputObject") ) {
- $bound = $true
- $totalCount = $inputObject.count
- }
- if ($Activity -ne 'Default') { Write-Progress -Id $ProgressId -Activity $Activity -Status "Creating threads" -CurrentOperation "Elapsed time: $($stopWatch.Elapsed.toString().subString(0,8))" }
- [int]$ProgressTotal = 0 # Total number of threads created
- [int]$ProgressCount = 0 # Number of threads completed
- $runtemplate = @'
- #For each pipeline object, create a new powershell instance, add to runspacepool
- $powershell = [powershell]::Create().AddScript($ScriptBlock).AddArgument($InputObject)
- $powershell.runspacepool=$pool
- $startTime = Get-Date
- #add references to inputobject, instance, handle and startTime to threads array
- $threads += New-Object psobject -Property @{
- Object = $inputObject;
- instance = $powershell;
- handle = $powershell.begininvoke();
- startTime = $startTime
- }
- $ProgressTotal++
- Write-Verbose "Added $inputobject to the runspacepool at $startTime"
- if ($Activity -ne 'Default') { Write-Progress -Id $ProgressId -Activity $Activity -Status "$ProgressTotal threads created" -CurrentOperation "Elapsed time: $($stopWatch.Elapsed.toString().subString(0,8))" -PercentComplete ([math]::Min($ProgressTotal,95)) }
- '@
- }
- PROCESS {
- if($bound) {
- $run = $runtemplate -replace 'inputObject', 'object'
- foreach($object in $inputObject) {
- Invoke-Expression -command $run
- }
- }
- else {
- Invoke-Expression -command $run
- }
- }
- END {
- $notdone = $true
- $ProgressTotal = $threads.count;
- $ProgressCount = 0
- if ($Activity -ne 'Default') { Write-Progress -Id $ProgressId -Activity $Activity -Status "$ProgressCount of $ProgressTotal threads completed" -CurrentOperation "Elapsed time: $($stopWatch.Elapsed.toString().subString(0,8))" -PercentComplete (100*$ProgressCount/$ProgressTotal) }
- #Loop through threads.
- while ($notdone) {
- $notdone = $false
- for ($i=0; $i -lt $threads.count; $i++) {
- $thread = $threads[$i]
- if ($thread) {
- #If thread is complete, dispose of it.
- if ($thread.handle.iscompleted) {
- Write-verbose "Closing thread for $($thread.Object)"
- $thread.instance.endinvoke($thread.handle)
- $thread.instance.dispose()
- $threads[$i] = $null
- $ProgressCount++
- }
- #Thread exceeded maxruntime timeout threshold
- elseif( $Timeout -ne 0 -and ( (get-date) - $thread.startTime ).totalminutes -gt $Timeout ) {
- Write-Error "Closing thread for $($thread.Object): Thread exceeded $Timeout minute limit" -TargetObject $thread.inputObject
- $thread.instance.dispose()
- $threads[$i] = $null
- $ProgressCount++
- }
- #Thread is running, loop again!
- else {
- $notdone = $true
- }
- }
- }
- if ($Activity -ne 'Default') {Write-Progress -Id $ProgressId -Activity $Activity -Status "$ProgressCount of $ProgressTotal threads completed" -CurrentOperation "Elapsed time: $($stopWatch.Elapsed.toString().subString(0,8))" -PercentComplete (100*$ProgressCount/$ProgressTotal) }
- Start-Sleep -Milliseconds $SleepTimer
- }
- if ($Activity -ne 'Default') { Write-Progress -Id $ProgressId -Activity $Activity -Status "Completed" -CurrentOperation "Elapsed time: $($stopWatch.Elapsed.toString().subString(0,8))" -Completed }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement