Summary: Boe Prox presents some tips about beginning use with runspaces.
Honorary Scripting Guy and Cloud and Datacenter Management MVP, Boe Prox, here today filling in for my good friend, The Scripting Guy.
Note This is a four-part series that includes the following posts:
- Beginning Use of PowerShell Runspaces: Part 1 Begin use with runspaces.
- Beginning Use of PowerShell Runspaces: Part 2 Begin use with runspaces.
- Beginning Use of PowerShell Runspaces: Part 3 Use runspace pools for multithreading.
- A Look at the PoshRSJob Module Learn about a module for working with runspaces.
Let’s say you are working in PowerShell and you want to kick off something to run in the background while you are working on some other things in the console. You would usually use something related to PSJobs, such as the *-Job cmdlets. Or you might use the –Asjob parameter that you see in some cmdlets, such as Get-WMIObject.
The main complaint in some of these efforts is that this causes another PowerShell process to be created and loaded with all of the format and type files, creating the PSDrives and such. This can take up a lot of resources depending on how many jobs you plan to run.
Using runspaces is a great alternative to get around this issue. Runspaces create a new thread on the existing process, and you can simply add what you need to it and send it off running. The downside is the level of effort that is required to create the runspace and the PowerShell instance to run it. Additionally, you have to manage the whole process by kicking off the instance and monitoring it for completion. Then you need to retrieve the information (if applicable) and tear down the runspace and PowerShell instance for disposal.
Using the built-in jobs framework makes this easy because you simply run something like Start-Job and then Receive-Job to get the data. Everything else happens behind the scenes. My plan is to take difficulty that may appear with using runspaces and show you how quickly and simply you can kick off a command in a runspace and pull it back without breaking a sweat.
To start off, I will make use of the [powershell] type accelerator and use the Create() method to quickly create the instance. From there, I’ll add a script block and kick off the instance. Note that this already has a runspace built-in, so I do not have to worry about creating a new runspace at this point.
$PowerShell = [powershell]::Create()
[void]$PowerShell.AddScript({
Get-Date
})
$PowerShell.Invoke()
The result of running this is simply the return of the current date and time. Also look at where I used [void] so that it doesn’t pollute my pipeline or anywhere in the output. What you may not know is that this is no different than what would run if I typed Get-Date in my console—which is not really useful if we want a way to run things in the background. Add a Start-Sleep –Seconds 10 in my example script block, and you will see what I mean.
Before I jump into some better multithreading examples, I am going to show the same example, but this time I am creating a runspace to work with and adding it to the PowerShell instance.
$Runspace = [runspacefactory]::CreateRunspace()
$PowerShell = [powershell]::Create()
$PowerShell.runspace = $Runspace
$Runspace.Open()
[void]$PowerShell.AddScript({
Get-Date
})
$PowerShell.Invoke()
I need a better way to run a command in the background so the console can be open to do other things. Instead of using Invoke() to kick off our commands, I will instead use BeginInvoke(). This will give back an async object, which can be used to monitor the state of our currently running background command.
Assuming that everything works out, we will eventually see that the background command has ended and I am free to retrieve the output and dispose of the PowerShell instance (so I do not have the possibility of memory leaks or something else).
First things first. Let’s kick off a background command and get back the object so we can track it.
$Runspace = [runspacefactory]::CreateRunspace()
$PowerShell = [powershell]::Create()
$PowerShell.runspace = $Runspace
$Runspace.Open()
[void]$PowerShell.AddScript({
Get-Date
Start-Sleep -Seconds 10
})
$AsyncObject = $PowerShell.BeginInvoke()
We can now inspect our Async object and view its status to see how things are going.
$AsyncObject
Based on the IsCompleted property, the command is currently running in the background, so I need to wait a little bit longer and then check again. When it shows True, I can begin processing the data and tearing down the instance.
Let’s assume that this has finally completed, and now I need to get the output. First I need to use the async object that was saved as the key to unlock the door on the PowerShell instance when I call EndInvoke(). As soon as I do this, the output is displayed. It is a good idea to save the output for future use.
$Data = $PowerShell.EndInvoke($AsyncObject)
Next up is to clean up by properly disposing of the instance. Nothing too crazy, but it is definitely necessary to ensure that I do not set myself up for disaster by having resource issues.
$PowerShell.Dispose()
And that is it for today and beginning with PowerShell runspaces. Tomorrow, I’ll build on what we learned by incorporating parameters and arguments into runspaces.
I invite you to follow the Scripting Guy on Twitter and Facebook. If you have any questions, send email to the Scripting Guy at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. Until then, see ya!
Boe Prox, Windows PowerShell MVP and Honorary Scripting Guy
Unable to create an empty powershell instance “$PowerShell = ::Create()”… just trying your example as is.
Getting an error “an expression was expected after ‘(‘ ”
I found the solution, believe it has to be like “$powershell = [Powershell]::Create()”