使用ForEach-Object -Parallel时变量返回空值,如何保留值?
解决ForEach-Object -Parallel中变量无法保留值的问题
问题根源
ForEach-Object -Parallel的每个迭代都跑在独立的PowerShell运行空间(线程)里,这些线程没法直接修改主会话里的变量。你直接写$sum += $_时,每个线程操作的都是自己本地的$sum副本,和主会话的$sum完全没关系;就算用了$using:sum也只能读取主变量的初始值,修改后也不会同步回主会话。
解决方案1:返回结果后在主会话汇总(推荐)
让每个并行线程返回当前元素的值,最后在主会话里统一汇总:
$numbers = @(1, 2, 3, 4, 5) $sum = $numbers | ForEach-Object -Parallel { # 返回当前元素,交给主会话处理 $_ } | Measure-Object -Sum | Select-Object -ExpandProperty Sum Write-Host $sum # 输出15
或者更简洁的写法:
$numbers = @(1,2,3,4,5) $sum = ($numbers | ForEach-Object -Parallel { $_ }) | ForEach-Object -Begin { $s=0 } -Process { $s += $_ } -End { $s } Write-Host $sum
解决方案2:用线程安全的原子操作直接累加
如果必须在并行线程里直接做累加,可以用[System.Threading.Interlocked]类的Add方法,它能保证多线程下的操作不会冲突:
$numbers = @(1, 2, 3, 4, 5) $sum = [ref]0 # 用ref类型传递变量引用 $numbers | ForEach-Object -Parallel { # 线程安全的累加操作 [System.Threading.Interlocked]::Add($using:sum, $_) } Write-Host $sum.Value # 输出15
这里必须用[ref]包装变量,再通过$using:传递引用,这样所有线程操作的是同一个内存地址的值,Interlocked.Add能避免多线程同时修改导致的计算错误。
内容的提问来源于stack exchange,提问作者Salva
相关产品推荐
相关产品推荐

