Multi-threading in PowerShell

There may be a time when you need to do things in parallel in PowerShell to accomplish a task.  I could spend my precious hours coming up with hypothetical examples of why this is needed or I could just give you the “need” that drove my desire to learn how to do this properly.
I needed to stand up a number of virtual machines in the Azure cloud as quickly as I could I could either do it manually using the Azure web interface or I could script it using the I chose the script route.
powershell-gsx-solutions-resized-600[1]
I did create a simple sample to give you the basic idea of how to go about it. While the following example doesn’t really do much, it should give you the basic understand of what is going on. This first example give you the idea of a single thread running.

$backgroundFunction = {
    Write-Host 'Starting'
    Start-Sleep 5
    Write-Host 'Half way done doing really important stuff'
    Start-Sleep 5
    Write-Host 'Exiting...'
}

function TestSingleJob {
    $job = Start-Job -Name "TestJob" -ScriptBlock $backgroundFunction
    While ($job.State -eq 'Running') {
        Start-Sleep 2
        Receive-Job -Job $job | Write-Host
        Write-Host 'Job is still running...'
    }
    Remove-Job $job -Force
    Write-Host 'Jobs Done'
}

You can run this code by typing

. .\scriptFileName.ps1
TestSingleJob

This is great, now I can do two things at once. But I wanted more, I needed to stand up almost 50 machines at a single pop and I didn’t want to be pushing software to and configuring the machines all day and all night. I needed to be able to multi-thread this bad boy. Once I had my prototype working, I started breaking up the code along lines of concern. Pushing out the software, configuring the web sites, setting up the application pool, etc.
Now instead of needing to know if a single background thread was running I need to know if ANY of them were running. I thought, “Wouldn’t it be nice if I could use LINQ .Any() from C# to help me out?” so I wrote one that helps me accomplish this task.

function Any {
    [CmdletBinding()]
    Param($evaluationCondition, [Parameter(ValueFromPipeline = $true)] $objectToTest)

    begin { $any = $false }
    process {
        if (-not $any -and (& $evaluationCondition $objectToTest)) {
            $any = $true
        }
    }
    end { $any }
}

This allowed me to see if any of the jobs were still running so that I could keep my PowerShell script in memory as long as one or more of them was still running.
Here is the sample that I cooked up that allowed me to demonstrate working with several background tasks at one time. This code uses the same background function as above, but I’ve excluded it for brevity.

function TestMultipleJobs {
    [array] $jobNames = @('Job 1', 'Job 2', 'Job 3', 'Job 4', 'Job 5')
    [array] $runningJobs = @()

    foreach($jobName in $jobNames) {
        $runningJobs += Start-Job -Name $jobName -ScriptBlock $backgroundFunction
    }

    While ($runningJobs | Any { $_.State -eq 'Running' }) {
        Start-Sleep 2
        Get-Job | Receive-Job | Write-Host
        Write-Host 'There are jobs still running...'
    }

    Remove-Job * -Force
    Write-Host 'All jobs have completed'
}

As you can see this will redirect any output from any of the running jobs to the Write-Host cmdlet. You can of course develop logic that will tell you what thread the message is coming from and format it to give you more information, etc.
I hope this gives you a peek into the world of multi-threading in PowerShell. Perhaps this can save you some time as it did for me. Until next time…

Advertisements

One thought on “Multi-threading in PowerShell

  1. Pingback: Creating Multiple Azure Virtual Machines Part Deux | OutOfMemoryException

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s