Изменение PowerShell с использованием переменных области видимости в потоке, запущенном с помощью Start-ThreadJob

Насколько я понимаю документация PowerShell Scopes, должна быть возможность назначать $using переменным области действия из потоков, запущенных с использованием Start-ThreadJob. В документации сказано (выделено моим):

Модификатор области действия Using поддерживается в следующих контекстах:

  • ...
  • Задания потока, запущенные через Start-ThreadJob или ForEach-Object -Parallel (отдельный сеанс потока)

В зависимости от контекста значения встроенных переменных являются либо независимыми копиями данных в области действия вызывающего объекта, либо ссылками на них.
...
В сеансах потоков они передаются по ссылке. Это означает, что переменные области вызова можно изменять в другом потоке. Для безопасного изменения переменных требуется синхронизация потоков.

Тем не менее, следующее не запускается:

$foo = 1

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

Write-Host $foo

Это ошибка на $using:foo = 2 с:

Недопустимое выражение присваивания. Входными данными для оператора присваивания должен быть объект, способный принимать присваивания, например переменная или свойство.

Печать переменной с Write-Host $using:foo работает корректно.

Я использую PowerShell 7.1.


person Martin Prikryl    schedule 24.03.2021    source источник


Ответы (1)


Вы не можете перезаписать ссылку на переменную $using:, но вы можете использовать ее для разыменования значения переменной в вызывающей области, после чего вы можете изменить ее (при условии, что значение ссылочного типа было присвоено исходной переменной):

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

Write-Host $foo.Value

Чтобы обеспечить синхронизацию потоков, я бы рекомендовал синхронизированную хеш-таблицу в качестве типа контейнера:

$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

В этот момент вы должны увидеть (увеличенное в 4 раза) значение 5

person Mathias R. Jessen    schedule 24.03.2021
comment
Спасибо за Ваш ответ. Да, я знаю это. Итак, вы думаете, что мое понимание того, что в документации означает передача по ссылке и возможность изменения... переменных, неверно? Потому что это не то, что я представляю при передаче по ссылке. Это больше похоже на передачу указателя по значению. - person Martin Prikryl; 24.03.2021
comment
@MartinPrikryl Я бы определенно сказал, что документация не очень хорошо объясняет, что передача по ссылке, вероятно, означает для вас что-то еще, если вы переходите с C#/VB.NET. Я не уверен, как бы это сформулировал лично я, но я призываю вас отправить отзыв о PowerShell. -Репозиторий документов - person Mathias R. Jessen; 24.03.2021
comment
Я отправил отзыв, как вы предложили: github.com/MicrosoftDocs/PowerShell-Docs /вопросы/7378 - person Martin Prikryl; 25.03.2021
comment
Кстати, этот синтаксис также работает: ($using:foo).Value = 2 - person Martin Prikryl; 25.03.2021