PoshCode Archive  Artifact [ee83c071fb]

Artifact ee83c071fb00c329a9ea813d4493668b482ac55996e188eadd8c4d366303e77e:

  • File Execute-RunspaceJob.ps1 — part of check-in [95db35bd32] at 2018-06-10 13:27:34 on branch trunk — A function to facilitate use of background runspaces in Powershell. (user: Josh Feierman size: 5155)

# encoding: ascii
# api: powershell
# title: Execute-RunspaceJob
# description: A function to facilitate use of background runspaces in Powershell.
# version: 1.1
# type: script
# author: Josh Feierman
# license: CC0
# function: Execute-RunspaceJob
# x-poshcode-id: 3554
# x-archived: 2012-08-04T23:34:45
# x-published: 2012-08-01T05:11:00
#
#
<#
.SYNOPSIS
   Executes a set of parameterized script blocks asynchronously using runspaces, and returns the resulting data.
.DESCRIPTION
  Encapsulates generic logic for using Powershell background runspaces to execute parameterized script blocks in an 
  efficient, multi-threaded fashion.
  
  For detailed examples of how to use the function, see http://awanderingmind.com/tag/execute-runspacejob/.
  
.PARAMETER ScriptBlock
   The script block to execute. Should contain one or more parameters.
.PARAMETER ArgumentList
  A hashtable containing data about the entity to be processed. The key should be a unique string to 
  identify the entity to be processed, such as a server name. The value should be another hashtable
  of arguments to be passed into the script block.
.PARAMETER ThrottleLimit
  The maximum number of concurrent threads to use. Defaults to 10.
.PARAMETER RunAsync
  If specified, the function will not wait for all the background runspaces to complete. Instead, it will return
  an array of runspace objects that can be used to further process the results at a later time.
.NOTES
  Author: Josh Feierman
  Date: 7/15/2012
  Version: 1.1
   
#>

function Execute-RunspaceJob
{
	[Cmdletbinding()]
	param
	(
		[parameter(mandatory=$true)]
		[System.Management.Automation.ScriptBlock]$ScriptBlock,
		[parameter(mandatory=$true,ValueFromPipeline=$true)]
		[System.Collections.Hashtable]$ArgumentList,
		[parameter(mandatory=$false)]
		[int]$ThrottleLimit = 10,
    [parameter(mandatory=$false)]
    [switch]$RunAsync
	)

	begin
  {
    try
    {
      #Instantiate runspace pool
      $runspacePool = [runspacefactory]::CreateRunspacePool(1,$ThrottleLimit)
      $runspacePool.Open()
      
      #Array to hold runspace data
      $runspaces = @()
      
      #Array to hold return data
      $data = @()
    }
    catch
    {
      Write-Warning "Error occurred initializing function setup."
      Write-Warning $_.Exception.Message
      break
    }
  }
  process
  {
    # Queue all sets of parameters for execution
    foreach ($Argument in $ArgumentList.Keys)
    {
      try
      {
        $rowIdentifier = $Argument
        Write-Verbose "Queuing item $rowIdentifier for processing."
        
        $runspaceRow = "" | Select-Object @{n="Key";e={$rowIdentifier}},
                                          @{n="Runspace";e={}},
                                          @{n="InvokeHandle";e={}}
        $powershell = [powershell]::Create()
        $powershell.RunspacePool = $runspacePool
        $powershell.AddScript($scriptBlock).AddParameters($ArgumentList[$rowIdentifier]) | Out-Null
        
        $runspaceRow.Runspace = $powershell
        $runspaceRow.InvokeHandle = $powershell.BeginInvoke()
        $runspaces += $runspaceRow
      }
      catch
      {
        Write-Warning "Error occurred queuing item '$Argument'."
        Write-Warning $_.Exception.Message
      }
    }
  }
  
  end
  {
    try
    {
      if ($RunAsync)
      {
        Write-Output $runspaces
      }
      else
      {
        $totalCount = $runspaces.Count
    
        # Wait for all runspaces to complete
        while (($runspaces | Where-Object {$_.InvokeHandle.IsCompleted -eq $false}).Count -gt 0)
        {
          $completedCount = ($runspaces | Where-Object {$_.InvokeHandle.IsCompleted -eq $true}).Count
          Write-Verbose "Completed $completedCount of $totalCount"
          Start-Sleep -Seconds 1
        }
        
        # Retrieve returned data and handle any threads that had errors
        foreach ($runspaceRow in $runspaces)
        {
          try
          {
            Write-Verbose "Retrieving data for item $($runspaceRow.Key)."
            if ($runspaceRow.Runspace.InvocationStateInfo.State -eq "Failed")
            {
              $errorMessage = $runspaceRow.Runspace.InvocationStateInfo.Reason.Message
              Write-Warning "Processing of item $($runspaceRow.Key) failed with error: $errorMessage"
            }
            else
            {
              $data += $runspaceRow.Runspace.EndInvoke($runspaceRow.InvokeHandle)
            }
          }
          catch
          {
            Write-Warning "Error occurred processing result of runspace for set '$($runspaceRow.Key)'."
            Write-Warning $_.Exception.Message
          }
          finally
          {
            $runspaceRow.Runspace.dispose()
          }
          
        }
        
        $runspacePool.Close()
        
        Write-Output $data
      }
    }
    catch
    {
      Write-Warning "Error occurred processing returns of runspaces."
      Write-Warning $_.Exception.Message
    }
  }
}