Background Runspaces and querying status

Good afternoon,

I’m currently banging my head against the desk trying to figure this out. I’m completely comfortable with runspaces… I use them all the time to throttle large amounts of data copies, etc… since you can set up a Runspace pool of 1-5 active tasks… even though you added 50 tasks to the list, only 5 at a time will run. And that’s great, i love it. Works quite well.

My problem is… I’m trying something new. I want to be able to query WHICH of the 50 tasks is actually running. So lets say i’ve got some computers I want to run commands against. I limit the number of active tasks to 3 but i’ve got 5 computers. As one of the first 3 finishes, #4 would start, etc… When I start my runspaces and add them to the pool, one of the properties I add is called ‘Computer’ and I can clearly see that when I view the $RunspaceCollection The problem i have here is that there’s no property for ‘active’ or ‘running’. The only thing I can find that is accurate is a property on each runspace item within the RunspaceCollection. I.E.:
$RunspaceColletion.runspace[0] will give me the details about the first task added to the pool. There is an ‘IsCompleted’ value of False or True. They all start out as false… and as each task finishes, they get marked IsCompleted = $True automatically. But I can’t tell which ones are actually running and it’s driving me nuts. I want to be able to update my screen saying COMPUTER1, COMPUTER2, and COMPUTER3 are currently copying… and update a status elsewhere in my code.

Confused yet? :stuck_out_tongue:

Example code that runs 5 computers through a 60 second sleep just for an example, so you have 60 seconds to look things over. If you run all of the code prior to the WHILE statement towards the bottom, you can look at the values of the different objects/variables.

$Computers = "COMPUTER1","COMPUTER2","COMPUTER3","COMPUTER4","COMPUTER5"

#Create empty Array
$RunspaceCollection = @()

#Open Define the number of processes the pool should allow.  minimum and maximum values (1 and 5)
$RunspacePool = [RunspaceFactory]::CreateRunspacePool(1,3)
$RunspacePool.Open()

#Define the actual work each runspace will perform.
$ScriptBlock = {

 Param($Computer)

#Get-Process -ComputerName $Computer -FilterHashtable @{Logname='System';Id=1074} -MaxEvents 150 
sleep 60

}

Foreach($Computer in $Computers){

   #Create a PowerShell object to run add the script and argument.

   $Powershell = [PowerShell]::Create().AddScript($ScriptBlock).AddArgument($Computer)
   
   #Specify runspace to use
   $Powershell.RunspacePool = $RunspacePool

   #Create Runspace collection
   [Collections.Arraylist]$RunspaceCollection += New-Object -TypeName PSObject -Property @{
   Runspace = $PowerShell.BeginInvoke()
   PowerShell = $PowerShell
   Computer = $Computer
   }
 }


#Check the RunSpace and validate that all of the tasks in the pool have completed.  Once completed, send 'EndInvoke' to each one.
#     Once all of the tasks are completed, remove the runspace.

 While($RunspaceCollection){

 Foreach($Runspace in $RunspaceCollection.ToArray()){

  If($Runspace.Runspace.IsCompleted){
   $Runspace.PowerShell.EndInvoke($Runspace.Runspace)
   $Runspace.PowerShell.Dispose()
   $RunspaceCollection.Remove($Runspace)
  }
 }
}

First I think - about [Hashtable]::Synchronized(@{})
and script looks like

[...]

$RunspacePool.Open()
$DataHash = [Hashtable]::Synchronized(@{})
$ScriptBlock = {

 Param($Computer, $DataHash)
    $DataHash[$Computer]='active'

#Get-Process -ComputerName $Computer -FilterHashtable @{Logname='System';Id=1074} -MaxEvents 150 
    sleep 60

    $DataHash.Remove($Computer)
}
Foreach($Computer in $Computers){

   #Create a PowerShell object to run add the script and argument.

   $Powershell = [PowerShell]::Create().AddScript($ScriptBlock).AddArgument($Computer).AddArgument($DataHash)

[...]

Not sure about right datahash parameter passing, may be it is need to pass with $runspace.SessionStateProxy.SetVariable(‘Hash’,$DataHash)

Max, that is pefect. There’s no need to use the SessionStateProxy portion in your last comment because it’s a Runspace Pool i’m using. I can just add it in like you noted using the .addArgument($hashtablename) and then include that $hashtablename in my Param() section of the scriptblock. I’ve never heard of ‘synchronized’ hashtables. This just blew my mind haha. Works GREAT!

As an example… i just threw a little bit of code into the While loop at the end so i could pretend each scriptblock was copying data. The 3 that are copying show copying instead of Pending, and after 15 seconds, they’re finished(and get marked as such)… and the remaining 2 switch from pending to Copying.

I’m ecstatic! Thanks!!!

$Computers = "COMPUTER1","COMPUTER2","COMPUTER3","COMPUTER4","COMPUTER5"

#Create empty Array
$RunspaceCollection = @()

#Open Define the number of processes the pool should allow.  minimum and maximum values (1 and 5)
$RunspacePool = [RunspaceFactory]::CreateRunspacePool(1,3)
$RunspacePool.Open()
$RunspaceHash = [hashtable]::Synchronized(@{})


#Define the actual work each runspace will perform.
$ScriptBlock = {

 Param($Computer,$RunspaceHash)

    $Runspacehash[$Computer].State = "Copying..."
sleep 15
   $Runspacehash[$Computer].State = "Finished"

}

Foreach($Computer in $Computers){

   #Create a PowerShell object to run add the script and argument.

   $Powershell = [PowerShell]::Create().AddScript($ScriptBlock).AddArgument($Computer).AddArgument($RunspaceHash)
   
   #Specify runspace to use
   $Powershell.RunspacePool = $RunspacePool

      $Runspacehash[$Computer] = @{"State"="Pending"}

   #Create Runspace collection
   [Collections.Arraylist]$RunspaceCollection += New-Object -TypeName PSObject -Property @{
   Runspace = $PowerShell.BeginInvoke()
   PowerShell = $PowerShell
   Computer = $Computer
   }
 }


#Check the RunSpace and validate that all of the tasks in the pool have completed.  Once completed, send 'EndInvoke' to each one.
#     Once all of the tasks are completed, remove the runspace.
$x = 0
 While($RunspaceCollection){
            $X++
            "$x"
            sleep 1
            cls
            foreach ($Entry in ($RunspaceHash.GetEnumerator() | sort))
            {
            $Entry.name
            $Runspacehash[$entry.name].state
            ""
            }


 Foreach($Runspace in $RunspaceCollection.ToArray()){

  If($Runspace.Runspace.IsCompleted){
   $Runspace.PowerShell.EndInvoke($Runspace.Runspace)
   $Runspace.PowerShell.Dispose()
   $RunspaceCollection.Remove($Runspace)
  }
 }
}