如何在PowerShell ThreadJobs中使用:variables分配值?

huangapple go评论71阅读模式
英文:

How do I assign values to using:variables in PowerShell ThreadJobs?

问题

ThreadJobs在启动时具有与其相同的环境。
但通常情况下,PowerShell在尝试更改来自父级的变量时会响应语法错误。

文档MS Learn - about_Thread_Jobs提供了一些见解,但我找不到有用的内容。

下面的示例说明了在尝试使用普通的PowerShell变量时出现的问题。

[Array]$Numbers = @()

foreach ($i in 0..11) {
    $Jobs = Start-ThreadJob {
        $Using:Numbers += $using:i
    }
}

$Jobs | Wait-Job | Remove-Job

$Numbers

ParserError:
行 |
   6 |          $Using:Numbers += $using:i
     |          ~~~~~~~~~~~~~~~
     | 分配表达式无效。分配运算符的输入必须是能够接受分配的对象,例如变量或属性。
英文:

ThreadJobs has access to the same environment as it was started in.
But normally, PowerShell will respond with a syntax error when trying to change variables from the parent level.

The documentation MS Learn - about_Thread_Jobs has som insights but nothing I could find useful.

The example below illustrates the issue when trying to use plain PowerShell variables.

[Array]$Numbers = @()

foreach ($i in 0..11) {
    $Jobs = Start-ThreadJob {
        $Using:Numbers += $using:i
    }
}

$Jobs | Wait-Job | Remove-Job

$Numbers

ParserError: 
Line |
   6 |          $Using:Numbers += $using:i
     |          ~~~~~~~~~~~~~~~
     | 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.

答案1

得分: 1

由于线程并行运行,必须有一种方法来防止父对象被破坏,或者在两个或更多线程尝试在对象上的确切同时执行操作时导致 ThreadJob 失败。

在与线程安全执行的概念搏斗了数天之后(并且在Santiago Squarzon的耐心帮助下以及其他人的帮助下),我的结论如下:

线程中的所有操作都必须是线程安全的(因此得名)。

  1. 如果对象是“普通”的话,不要尝试使用 Using: 设置值,除非您能够保证该值已被锁定。
  2. 即使使用线程安全对象,也不要尝试在两个操作之间保证值已被锁定的情况下同时读取和写入值。
  3. 除非您能够保证值已被锁定,否则只使用提供的线程安全方法来操作线程安全对象中的数据。

在 .Net 中,我找到了两个可以使用的线程安全类别:

(每个 T 引用都有一个类名可用。)

这些类中都没有用于递增值的线程安全方法。

因此,在 PowerShell 7.x 中,一个线程安全的 ThreadJob,仅将新项添加到父对象,可能如下所示:

$SafeNumbers = [System.Collections.Concurrent.ConcurrentBag[object]]::new()

foreach ($i in 0..11) {
    $Thread = Start-ThreadJob {
        ($Using:SafeNumbers).Add($Using:i)
    }
}

Get-Job | Wait-Job | Remove-Job

$SafeNumbers.ToArray()

11
10
9
8
7
6
5
4
3
2
1
0

当然,输出的顺序不能保证。

英文:

As the threads are run in parallell, there has to be some kind of method that prevents the parent object from getting corrupted or a ThreadJob from failing if two or more threads tries to perform operations at the exact same time on the object.

After wrestling several days with the concept of thread safe execution (and getting great help with the patience of Santiago Squarzon
and others, my own conclusion is:

All the operation in the thread has to be made thread safe (hence the name).

  1. Don't try setting values with Using: if the objects are "plain" unless you can guarantee the value has been locked
  2. Even if using thread safe objects, don't try to both read and write of the value unless you can guarantee the value has been locked in between the two operations
  3. Only use the provided thread safe methods for manipulating data in the thread safe objects unless you can guarantee the value has been locked

In .Net, I found two thread safe classes you can work with

(There is one class name available per T-reference.)

None of the classes has a thread safe method for incrementing values.

So a thread safe ThreadJob in PowerShell 7.x, only adding new items to parent objects, might look like this

$SafeNumbers = [System.Collections.Concurrent.ConcurrentBag[object]]::new()

foreach ($i in 0..11) {
    $Thread = Start-ThreadJob {
        ($Using:SafeNumbers).Add($Using:i)
    }
}

Get-Job | Wait-Job | Remove-Job

$SafeNumbers.ToArray()

11
10
9
8
7
6
5
4
3
2
1
0

The order of the output is of course not guaranteed.

答案2

得分: 0

在一些搜索后,我找到了一个看起来变量引用似乎可以工作的解决方法...

但迟早会失败,所以不要使用这个示例。

[Array]$Numbers = @()
$refNumbersVar = Get-Variable Numbers

foreach ($i in 0..11) {
    $Jobs = Start-ThreadJob {
        ($Using:refNumbersVar).Value += $using:i
    }
}

$Jobs | Wait-Job | Remove-Job

$Numbers

编辑:已警告所有人不要使用这个示例。

英文:

After some searching, I found a work around where variable referrals apparently works...

But it will fail sooner or later, so DON´T use the example.

[Array]$Numbers = @()
$refNumbersVar = Get-Variable Numbers

foreach ($i in 0..11) {
    $Jobs = Start-ThreadJob {
        ($Using:refNumbersVar).Value += $using:i
    }
}

$Jobs | Wait-Job | Remove-Job

$Numbers

Edit: Warned all to not use this example.

huangapple
  • 本文由 发表于 2023年6月22日 20:15:59
  • 转载请务必保留本文链接:https://go.coder-hub.com/76531794.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定