1

The code below isolates the task I am trying to do. Once a number over 3 is checked in $loopMe, I want the foreach-object loop to end across all runspaces and that the value of numberOverLimit is set to true.

$loopMe = [System.Collections.ArrayList]@()
for($index = 0; $index -lt 5; $index++){
  $loopMe.Add($index)>$null;
}
$global:numberOverLimit=$false
$addToMe= [System.Collections.Concurrent.ConcurrentBag[psobject]]::new() 
$loopMe | Foreach-Object -ThrottleLimit 6 -Parallel{
  $localAddToMe=$using:addToMe
  Write-Host $_
  if($_ -gt 3){
    $global:numberOverLimit=$true
    break
  }
  $localAddToMe.Add($_)
}
write-Host $addToMe
Write-Host $numberOverLimit
if($numberOverLimit){
    Write-Host "A number was over the limit"
    exit
}
else{
    Write-Host "All is good"
}

Expected Result

0
1
2
3
3 2 1 0
True
A number was over the limit

Actual Result

0
1
4
2
3
3 2 1 0
False
All is good
OrigamiEye
  • 864
  • 1
  • 12
  • 31

1 Answers1

1

Your parallel loop can't see your $global:numberOverLimit variable and even if it would, it wouldn't be able to update the boolean (a value type). You can only update a reference type from your parallel loop, which is why I'm using Get-Variable in this example.

Also note, the use of break in a script block:

Using break inside a pipeline break, such as a ForEach-Object script block, not only exits the pipeline, it potentially terminates the entire runspace.

The only built-in method to stop a pipeline early is with Select-Object -First, you can pipe your parallel loop to it and output anything to stdout to terminate your parallel loop:

Lastly, you should ensure thread safety before updating your PSVariable instance, for that you need to use some sort of locking mechanism, Monitor.Enter is the one used in this case.

$addToMe = [System.Collections.Concurrent.ConcurrentBag[psobject]]::new()
$numberOverLimit = $false
$psvar = Get-Variable numberOverLimit

0..5 | ForEach-Object -ThrottleLimit 6 -Parallel {
    $localAddToMe = $using:addToMe
    $psvar = $using:psvar

    Write-Host $_

    if($_ -gt 3) {
        [System.Threading.Monitor]::Enter($psvar)
        $psvar.Value = $true
        [System.Threading.Monitor]::Exit($psvar)
        return 'something, whatever here'
    }

    $localAddToMe.Add($_)
} | Select-Object -First 1 | Out-Null

Write-Host $addToMe
Write-Host $numberOverLimit
if($numberOverLimit) {
    Write-Host 'A number was over the limit'
    return
}
else {
    Write-Host 'All is good'
}
Santiago Squarzon
  • 41,465
  • 5
  • 14
  • 37
  • 1
    Just to check I understood it. The Select-Object -First works because only when there is a value gt 3 is there an object that is returned. Otherwise it runs through all the vaules. – OrigamiEye Apr 26 '23 at 07:06
  • 1
    thats correct @OrigamiEye the only produce output from your loop comes from that `if` condition – Santiago Squarzon Apr 26 '23 at 12:06