Thread Synchronization (in PowerShell?)

Up until now, I had been under the impression that there’s no need to worry about synchronized access to objects in PowerShell, even when using runspaces. (See Jason Shirk’s comment on this stackoverflow post.) Reading today’s Hey, Scripting Guy! blog post by Boe Prox on using WPF with PowerShell, it would appear I was mistaken. He’s using the [hashtable]::Synchronize() method to obtain a thread-safe wrapper to a hashtable, and passing that object to other runspaces by way of the Runspace.SessionStateProxy.SetVariable() method.

Since this would imply that it’s possible to access objects from different threads in a PowerShell session, I thought it might be useful to have a “Lock” statement in PowerShell as well. mentions that you must explicitly lock the SyncRoot property of an Collection to perform a thread-safe enumeration of its contents, even if you’re dealing with a Synchronized collection. We’d like to be able to do this:

$myHashTable = @{
    One   = 1
    Two   = 2
    Three = 3

lock ($myHashTable.SyncRoot) {
    foreach ($keyValuePair in $myHashTable.GetEnumerator())
        # Do something

Edit: I realized today that while the source code was displaying quite well on WordPress, line breaks weren’t being preserved when copying and pasting code off the page. I’ve uploaded the function to the TechNet Gallery at . Also, I removed pipeline support from the function for now, after running into some problems getting it working properly.

Here’s the function:

function Lock-Object
       Locks an object to prevent simultaneous access from another thread.
       PowerShell implementation of C#'s "lock" statement.  Code executed in the script block does not have to worry about simultaneous modification of the object by code in another thread.
    .PARAMETER InputObject
       The object which is to be locked.  This does not necessarily need to be the actual object you want to access; it's common for an object to expose a property which is used for this purpose, such as the ICollection.SyncRoot property.
    .PARAMETER ScriptBlock
       The script block that is to be executed while you have a lock on the object.
       Note:  This script block is "dot-sourced" to run in the same scope as the caller.  This allows you to assign variables inside the script block and have them be available to your script or function after the end of the lock block, if desired.
       $hashTable = @{}
       lock $hashTable.SyncRoot {
           $hashTable.Add("Key", "Value")

       This is an example of using the "lock" alias to Lock-Object, in a manner that most closely resembles the similar C# syntax with positional parameters.
       $hashTable = @{}
       Lock-Object -InputObject $hashTable.SyncRoot -ScriptBlock {
           $hashTable.Add("Key", "Value")

       This is the same as Example 1, but using the full PowerShell command and parameter names.
       None.  This command does not accept pipeline input.
       System.Object (depends on what's in the script block.)
        Most of the time, PowerShell code runs in a single thread.  You have to go through several steps to create a situation in which multiple threads can try to access the same .NET object.  In the Links section of this help topic, there is a blog post by Boe Prox which demonstrates this.

    param (
        [Parameter(Mandatory = $true, Position = 0)]

        [Parameter(Mandatory = $true, Position = 1)]

    if ($InputObject.GetType().IsValueType)
        $params = @{
            Message      = "Lock object cannot be a value type."
            TargetObject = $InputObject
            Category     = [System.Management.Automation.ErrorCategory]::InvalidArgument
            ErrorId      = 'CannotLockValueType'

        Write-Error @params

    $lockTaken = $false

        $lockTaken = $true
        . $ScriptBlock
        $params = @{
            Exception    = $_.Exception
            Category     = [System.Management.Automation.ErrorCategory]::OperationStopped
            ErrorId      = 'InvokeWithLockError'
            TargetObject = New-Object psobject -Property @{
                ScriptBlock  = $ScriptBlock
                ArgumentList = $ArgumentList
                InputObject  = $InputObject
                LockProperty = $LockProperty

        Write-Error @params
        if ($lockTaken)

Set-Alias -Name Lock -Value Lock-Object

# Export-ModuleMember -Function Lock-Object -Alias Lock

And some demonstration code (which requires the Lock-Object function to be in a file named PSThreading.psm1 in the current working directory):

$arrayList = New-Object System.Collections.ArrayList

$sessionstate = []::CreateDefault()
    (New-Object System.Management.Automation.Runspaces.SessionStateVariableEntry('arrayList', $arrayList, $null))

$runspacepool = [runspacefactory]::CreateRunspacePool(1, 2, $sessionstate, $Host)

$ps1 = [powershell]::Create()
$ps1.RunspacePool = $runspacepool

$null = $ps1.AddScript({
    Lock-Object $arrayList.SyncRoot {
        for ($i = 1; $i -le 5; $i++)
            Start-Sleep -Seconds 1
            $null = $arrayList.Add($i)

$handle1 = $ps1.BeginInvoke()

$ps2 = [powershell]::Create()
$ps2.RunspacePool = $runspacepool

$null = $ps2.AddScript({
    Lock-Object $arrayList.SyncRoot {
        foreach ($i in $arrayList)

$handle2 = $ps2.BeginInvoke()

if ([System.Threading.WaitHandle]::WaitAll($handle1.AsyncWaitHandle) -and

About Dave Wyatt

Microsoft MVP (PowerShell), IT professional.
This entry was posted in PowerShell. Bookmark the permalink.

One Response to Thread Synchronization (in PowerShell?)

  1. Pingback: Thread Synchronization (lock statement) in PowerShell |

Leave a Reply

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

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

Google+ photo

You are commenting using your Google+ 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 )


Connecting to %s