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.
Get-Service | Where-Object { $_.Status -eq 'Running' }
The simplest form of this Cmdlet is to pass in a script block.
$jobs = @();
$jobs += Start-Job -ScriptBlock
{ Get-Service | Where-Object { $_.Status -eq 'Running' } }
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.
#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
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.
$args = @()
$args += 'Running'
$jobs += Start-Job -ScriptBlock { Get-Service | Where-Object { $_.Status -eq "$($args[0])" } } -ArgumentList $args
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.
[scriptblock]$func = {
function Get-RunningService
{
Get-Service | Where-Object { $_.Status -eq "Running" }
}
}
Then we can create our job like so.
$jobs += Start-Job -ScriptBlock { Get-RunningService }
-InitializationScript $func
When we bring this all together we have an easy to remember asynchronous process.
$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
Written by Derik Hammer of SQL Hammer
Derik is a data professional focusing on Microsoft SQL Server. His passion focuses around high-availability, disaster recovery, continuous integration, and automated maintenance. his experience has spanned long-term database administration, consulting, and entrepreneurial ventures.
Derik gives the SQL community credit for plugging the gaps in his knowledge when he was a junior DBA and, now that his skills have matured, started SQLHammer.com as one small way to give back and continue the cycle of shared learning.
Derik is the owner and lead author of SQL Hammer, a Microsoft SQL Server resource.
For more information, visit http://www.sqlhammer.com. Follow Derik on Twitter for SQL tips and chat

Leave a Reply