2

If I understand the PowerShell Scopes documentation, it should be possible to assign to $using scope variables from threads started using Start-ThreadJob. The documentation says (emphasis mine):

The Using scope modifier is supported in the following contexts:

  • ...
  • Thread jobs, started via Start-ThreadJob or ForEach-Object -Parallel (separate thread session)

Depending on the context, embedded variable values are either independent copies of the data in the caller's scope or references to it.
...
In thread sessions, they are passed by reference. This means it is possible to modify call scope variables in a different thread. To safely modify variables requires thread synchronization.

Yet the following fails to run:

$foo = 1

Start-ThreadJob {
    Write-Host $using:foo
    $using:foo = 2
} | Wait-Job | Out-Null

Write-Host $foo

It errors on $using:foo = 2 with:

The assignment expression is not valid. The input to an assignment operator must be an object that is able to accept assignments, such as a variable or a property.

Printing the variable with Write-Host $using:foo works correctly.

I'm using PowerShell 7.1.

Martin Prikryl
  • 188,800
  • 56
  • 490
  • 992

1 Answers1

2

You can't overwrite the $using: variable reference - but you can use it to dereference a variable value in the calling scope, at which point you can then mutate it (assuming a reference-type value was assigned to the original variable):

$foo = @{ 
  Value = 1
}
Start-ThreadJob {
    Write-Host $using:foo
    $foo = $using:foo
    $foo.Value = 2
} | Wait-Job | Out-Null

Write-Host $foo.Value

To ensure thread synchronization, I'd recommended a synchronized hashtable as your container type:

$foo = [hashtable]::Synchronized(@{ 
  Value = 1
})

1..4 |%{Start-ThreadJob {
    Write-Host $using:foo
    $foo = $using:foo
    $foo.Value++
}} | Wait-Job | Out-Null

Write-Host $foo.Value

At which point you should see the (4-times incremented) value of 5

Mathias R. Jessen
  • 157,619
  • 12
  • 148
  • 206
  • Thanks for your answer. Yes, I know that. So do you think that my understanding of what the documentation means by *"passed by reference"* and *"possible to modify ... variables"* is wrong? Because this is not what I imagine by *"pass by reference"*. That's more like passing a pointer *by value*. – Martin Prikryl Mar 24 '21 at 15:50
  • 2
    @MartinPrikryl I'd certainly say that the documentation does not do a very good job of succinctly explaining "pass-by-reference probably means something else to you if you're coming from C#/VB.NET". I'm unsure how I'd personally phrase it, but I encourage you to [submit feedback on the PowerShell-Docs repo](https://github.com/MicrosoftDocs/PowerShell-Docs/issues) – Mathias R. Jessen Mar 24 '21 at 15:55
  • 1
    I've submitted a feedback as you have suggested: https://github.com/MicrosoftDocs/PowerShell-Docs/issues/7378 – Martin Prikryl Mar 25 '21 at 07:44
  • Btw, this syntax also works: `($using:foo).Value = 2` – Martin Prikryl Mar 25 '21 at 08:03