The Start-Job Cmdlet is a fast and extremely simple means of spawning asynchronous threads. Why do we care? Because performing parallel work is faster and our time is important. If I want to inventory the Windows services running on every server in my company, I don’t want to wait for each one to return data, one at a time. Before I move on with the Start-Job Cmdlet, there are two other notable concurrency methods that I won’t be covering in this post; runspaces, and work-flows.
To demonstrate I will use the Windows services task that I mentioned above. Here is how we can find a list of all of the Windows services running on a single server.
[sql]Get-Service | Where-Object { $_.Status -eq ‘Running’ }[/sql]
The simplest form of this Cmdlet is to pass in a script block.
[sql]$jobs = @();
$jobs += Start-Job -ScriptBlock `
{ Get-Service | Where-Object { $_.Status -eq ‘Running’ } }[/sql]
The Start-Job Cmdlet returns a System.Management.Automation.Job object which can be used for receiving the job output, waiting for thread completion, and disposing of the thread.
[sql]#wait for all threads in the array to complete and print their output.
$jobs | Wait-Job | Receive-Job
#dispose of your jobs
$jobs | Remove-Job[/sql]
If you would like to pass in parameters to your script block you can create an argument array. In the script block you must use the $arg[n] syntax, as shown.
[sql]$args = @()
$args += ‘Running’
$jobs += Start-Job -ScriptBlock { Get-Service | Where-Object { $_.Status -eq “$($args[0])” } } -ArgumentList $args[/sql]
Finally, I will demonstrate how to initialize the job with a script. This is often used to pre-load functions to be used in the script block. Let us say that I’ve created a function and saved it as a scriptblock.
[sql][scriptblock]$func = {
function Get-RunningService
{
Get-Service | Where-Object { $_.Status -eq “Running” }
}
}[/sql]
Then we can create our job like so.
[sql]$jobs += Start-Job -ScriptBlock { Get-RunningService } `
-InitializationScript $func[/sql]
When we bring this all together we have an easy to remember asynchronous process.
[sql]$jobs = @();
$sb = [scriptblock] { write-host “args = $($args[0])”; Invoke-Command -ComputerName ($args[0]) -ScriptBlock { Get-RunningService } }
foreach ($svr in ($servers.Name))
{
$args = @($svr);
$jobs += Start-Job -ScriptBlock $sb `
-InitializationScript $func `
-ArgumentList $args
}
$jobs | Wait-Job | Receive-Job
$jobs | Remove-Job[/sql]
Leave a Reply