gpt4 book ai didi

powershell - 如何将项目动态添加到 PowerShell ArrayList 并使用运行空间池递归处理它们?

转载 作者:行者123 更新时间:2023-12-05 03:22:27 24 4
gpt4 key购买 nike

我有一个 for 循环,它遍历 ArrayList 并在此过程中向列表添加更多项目并(迭代地)处理它们。我正在尝试使用 Runspacepool 将此函数转换为同时运行。

这是没有运行空间的正常代码:

$array = [System.Collections.ArrayList]@(1, 2, 3, 4, 5)
Write-Host "Number of items in array before loop: $($array.Count)"
for ($i = 0; $i -lt $array.Count; $i++) {
Write-Host "Counter: $i`tArray: $array"
if ($array[$i] -in @(1, 2, 3, 4, 5)) {
$array.Add($array[$i] + 3) | Out-Null
}
}
Write-Host "Array: $array"
Write-Host "Number of items in array after loop: $($array.Count)"

输出是:

Number of items in array before loop: 5
Counter: 0 Array: 1 2 3 4 5
Counter: 1 Array: 1 2 3 4 5 4
Counter: 2 Array: 1 2 3 4 5 4 5
Counter: 3 Array: 1 2 3 4 5 4 5 6
Counter: 4 Array: 1 2 3 4 5 4 5 6 7
Counter: 5 Array: 1 2 3 4 5 4 5 6 7 8
Counter: 6 Array: 1 2 3 4 5 4 5 6 7 8 7
Counter: 7 Array: 1 2 3 4 5 4 5 6 7 8 7 8
Counter: 8 Array: 1 2 3 4 5 4 5 6 7 8 7 8
Counter: 9 Array: 1 2 3 4 5 4 5 6 7 8 7 8
Counter: 10 Array: 1 2 3 4 5 4 5 6 7 8 7 8
Counter: 11 Array: 1 2 3 4 5 4 5 6 7 8 7 8
Array: 1 2 3 4 5 4 5 6 7 8 7 8
Number of items in array after loop: 12

这是 Runspace function我正在尝试实现:

$pool = [RunspaceFactory]::CreateRunspacePool(1, 10)
$pool.Open()
$runspaces = @()

$scriptblock = {
Param ($i, $array)
# Start-Sleep 1 # <------ Output varies significantly if this is enabled
Write-Output "$i value: $array"
if ($i -in @(1, 2, 3, 4, 5)) {
$array.Add($i + 3) | Out-Null
}
}

$array = [System.Collections.ArrayList]::Synchronized(([System.Collections.ArrayList]$(1, 2, 3, 4, 5)))
Write-Host "Number of items in array before loop: $($array.Count)"
for ($i = 0; $i -lt $array.Count; $i++) {
$runspace = [PowerShell]::Create().AddScript($scriptblock).AddArgument($array[$i]).AddArgument($array)
$runspace.RunspacePool = $pool
$runspaces += [PSCustomObject]@{ Pipe = $runspace; Status = $runspace.BeginInvoke() }
}

while ($runspaces.Status -ne $null) {
$completed = $runspaces | Where-Object { $_.Status.IsCompleted -eq $true }
foreach ($runspace in $completed) {
$runspace.Pipe.EndInvoke($runspace.Status)
$runspace.Status = $null
}
}
Write-Host "array: $array"
Write-Host "Number of items in array after loop: $($array.Count)"
$pool.Close()
$pool.Dispose()

没有 sleep 功能的输出符合预期:

Number of items in array before loop: 5
Current value: 1 Array: 1 2 3 4 5
Current value: 2 Array: 1 2 3 4 5 4
Current value: 3 Array: 1 2 3 4 5 4 5
Current value: 4 Array: 1 2 3 4 5 4 5 6
Current value: 5 Array: 1 2 3 4 5 4 5 6 7
Current value: 4 Array: 1 2 3 4 5 4 5 6 7 8
Current value: 5 Array: 1 2 3 4 5 4 5 6 7 8 7
Current value: 6 Array: 1 2 3 4 5 4 5 6 7 8 7
Current value: 7 Array: 1 2 3 4 5 4 5 6 7 8 7
Current value: 8 Array: 1 2 3 4 5 4 5 6 7 8 7
Current value: 7 Array: 1 2 3 4 5 4 5 6 7 8 7 8
Current value: 8 Array: 1 2 3 4 5 4 5 6 7 8 7 8
Array: 1 2 3 4 5 4 5 6 7 8 7 8
Number of items in array after loop: 12

sleep 输出:

Number of items in array before loop: 5
Current value: 1 Array: 1 2 3 4 5
Current value: 2 Array: 1 2 3 4 5 4
Current value: 3 Array: 1 2 3 4 5 4 5
Current value: 4 Array: 1 2 3 4 5 4 5 6
Current value: 5 Array: 1 2 3 4 5 4 5 6 7
Array: 1 2 3 4 5 4 5 6 7 8
Number of items in array after loop: 10

我知道发生这种情况是因为 for 循环在 sleep 时间完成之前退出,因此只有前 5 个项目被添加到运行空间池中。

有没有办法动态地向 ArrayList 添加更多项,并且仍然使用运行空间并发处理它们?

最佳答案

您“工作”行为的核心是 PowerShell 运行您的“非 sleep ”脚本 block 的速度快于它在 for创建它们的速度循环,所以循环看到在到达数组末尾之前由先前迭代添加的新项目。结果,它必须在退出并移至 while 之前处理所有 项。循环。

当您添加 Start-Sleep它改变了平衡,运行脚本 block 比创建脚本 block 花费的时间更长,所以for在最早的迭代添加新项之前,循环已到达数组末尾。

以下脚本通过结合您的 for 解决了这个问题和 while循环在 (i) 创建新线程和 (ii) 检查它们是否已完成之间反复交替,并且仅在所有工作完成时退出。

但是多线程很难,所以最好假设我在某处犯了错误,并在将其发布到您的实时工作流程之前进行正确测试...

$scriptblock = {
Param ($i, $array)
# random sleep to simulate variable-length workloads. this is
# more likely to flush out error conditions than a fixed sleep
# period as threads will finish out-of-turn more often
Start-Sleep (Get-Random -Minimum 1 -Maximum 10)
Write-Output "$i value: $array"
if ($i -in @(1, 2, 3, 4, 5)) {
$array.Add($i + 3) | Out-Null
}
}

$pool = [RunspaceFactory]::CreateRunspacePool(1, 10)
$pool.Open()

# note - your "$runspaces" variable is misleading as you're creating
# "PowerShell" objects, and a "Runspace" is a different thing entirely,
# so I've called it $instances instead
# see https://learn.microsoft.com/en-us/dotnet/api/system.management.automation.powershell?view=powershellsdk-7.0.0
# vs https://learn.microsoft.com/en-us/dotnet/api/system.management.automation.runspaces.runspace?view=powershellsdk-7.0.0
$instances = @()

$array = [System.Collections.ArrayList]::Synchronized(([System.Collections.ArrayList]$(1, 2, 3, 4, 5)))
Write-Host "Number of items in array before loop: $($array.Count)"

while( $true )
{

# start PowerShell instances for any items in $array that don't already have one.
# on the first iteration this will seed the initial instances, and in
# subsequent iterations it will create new instances for items added to
# $array since the last iteration.
while( $instances.Length -lt $array.Count )
{
$instance = [PowerShell]::Create().AddScript($scriptblock).AddArgument($array[$instances.Length]).AddArgument($array);
$instance.RunspacePool = $pool
$instances += [PSCustomObject]@{ Value = $instance; Status = $instance.BeginInvoke() }
}

# watch out because there's a race condition here. it'll need very unlucky
# timing, *but* an instance might have added an item to $array just after
# the while loop finished, but before the next line runs, so there *could*
# be an item in $array that hasn't had an instance created for it even
# if all the current instances have completed

# is there any more work to do? (try to mitigate the race condition
# by checking again for any items in $array that don't have an instance
# created for them)
$active = @( $instances | Where-Object { -not $_.Status.IsCompleted } )
if( ($active.Length -eq 0) -and ($instances.Length -eq $array.Count) )
{
# instances have been created for every item in $array,
# *and* they've run to completion, so there's no more work to do
break;
}

# if there are incomplete instances, wait for a short time to let them run
# (this is to avoid a "busy wait" - https://en.wikipedia.org/wiki/Busy_waiting)
Start-Sleep -Milliseconds 250;

}

# all the instances have completed, so end them
foreach ($instance in $instances)
{
$instance.Value.EndInvoke($instance.Status);
}

Write-Host "array: $array"
Write-Host "Number of items in array after loop: $($array.Count)"
$pool.Close()
$pool.Dispose()

示例输出:

Number of items in array before loop: 5
1 value: 1 2 3 4 5 6 5 7
2 value: 1 2 3 4 5 6
3 value: 1 2 3 4 5
4 value: 1 2 3 4 5 6 5
5 value: 1 2 3 4 5 6 5 7 4
6 value: 1 2 3 4 5 6 5 7
5 value: 1 2 3 4 5 6 5 7 4 8
7 value: 1 2 3 4 5 6 5 7
4 value: 1 2 3 4 5 6 5 7 4 8 8
8 value: 1 2 3 4 5 6 5 7 4 8 8
8 value: 1 2 3 4 5 6 5 7 4 8 8
7 value: 1 2 3 4 5 6 5 7 4 8 8 7

请注意,数组中项目的顺序将根据 $scriptblock 中随机 sleep 的长度而变化。 .

可能还可以进行其他改进,但这至少看起来可行...

关于powershell - 如何将项目动态添加到 PowerShell ArrayList 并使用运行空间池递归处理它们?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/72725891/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com