Synchronized Collections

As an example I’m passing a synchronized hash table into a powershell job.

> Write-Host "PSJobs with a synchronized collection"
> $hash = [hashtable]::Synchronized(@{})
> $hash.One = 1

> Write-host ('Value of $Hash.One before PSjob is {0}' -f $hash.one)
Value of $Hash.One before PSjob is 1

> Start-Job -Name TestSync -ScriptBlock {
    Param ($hash)
    $hash.One++
} -ArgumentList $hash | Out-Null

> Get-Job -Name TestSync | Wait-Job | Out-Null

> Write-host ('Value of $Hash.One after PSjob is {0}' -f $hash.one)
Value of $Hash.One after PSjob is 1

Get-Job | Remove-Job

As expected the original hash tables stays unmodified.
This is because ‘Start-Job’ spawns a new powershell.

Synchronized hash table with a single runspace

We will now create a runspace add our hash table to it, add a script block.
After that we add our runspace to a newly created Powershell instance and run it.
Waiting for the script to finish before cleaning up and writing our values to the console.

$hash = [hashtable]::Synchronized(@{})
$hash.One = 1

Write-host ('Value of $Hash.One before background runspace is {0}' -f $hash.one)
$runspace = [runspacefactory]::CreateRunspace()
$runspace.Open()

$runspace.SessionStateProxy.SetVariable('Hash',$hash)

$powershell = [powershell]::Create()
$powershell.Runspace = $runspace

$powershell.AddScript({
    $hash.one++
}) | Out-Null

$handle = $powershell.BeginInvoke()
While (-Not $handle.IsCompleted) {
    Start-Sleep -Milliseconds 100
}

$powershell.EndInvoke($handle)
$runspace.Close()
$powershell.Dispose()

Write-host ('Value of $Hash.One after background runspace is {0}' -f $hash.one)

Output

Value of $Hash.One before background runspace is 1
Value of $Hash.One after background runspace is 2

Example

Let’s add some more values that might be useful.
Look at the ‘Host’ value where the host runspace is passed to created runspace.
Also, take note of the 10 seconds ‘Start-Sleep’ which gives me the time to manipulate values from the parent session

$hash = [hashtable]::Synchronized(@{})
$hash.value = 1
$hash.Flag = $True
$hash.Host = $host

Write-host ('Value of $Hash.value before background runspace is {0}' -f $hash.value)

$runspace = [runspacefactory]::CreateRunspace()
$runspace.Open()
$runspace.SessionStateProxy.SetVariable('Hash',$hash)

$powershell = [powershell]::Create()
$powershell.Runspace = $runspace
$powershell.AddScript({
    While ($hash.Flag) {
        $hash.value++
        $hash.Services = Get-Service
        $hash.host.ui.WriteVerboseLine($hash.value)
        Start-Sleep -Seconds 10
    }
}) | Out-Null

$handle = $powershell.BeginInvoke()

Output parent session

VERBOSE 2
VERBOSE 3
...

Manipulate values from parent session during runtime

VERBOSE 7
> $hash.value = -10
VERBOSE -9
VERBOSE -8
VERBOSE -7
...
> $hash.Flag = $false

Setting $hash.Flag = $false will break the loop.

Source

This has been stolen here: https://learn-powershell.net/2013/04/19/sharing-variables-and-live-objects-between-powershell-runspaces/

Leave a Reply