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:
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.
由于线程是并行运行的,因此如果两个或多个线程尝试同时对对象执行操作,则必须有某种方法来防止父对象损坏或 ThreadJob 失败。
在与线程安全执行的概念进行了几天的斗争之后(并在 Santiago Squarzon 的耐心下得到了很大的帮助) 和其他人,我自己的结论是:
线程中的所有操作都必须是线程安全的(因此得名)。
Using:
设置值,除非您可以保证该值已被锁定在.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
当然不能保证输出的顺序。
经过一番搜索,我找到了一个解决变量引用显然有效的方法......
但是它迟早会失败,所以不要使用这个例子。
[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
编辑:警告所有人不要使用此示例。