Search code examples
powershellforeach

ForEach-Object -Parallel within foreach returning different result


Dears,

follwoing code, returs different/random(?) results.

Does anyone have an idea why?

Thank you!

$contacts = 200001..200064
while(1){
    $result = @{CSV=@()};
    foreach($contact in $contacts){
        $contacts | ForEach-Object -Parallel {
            ($using:result).CSV += "$($using:contact);$_"
        } -ThrottleLimit 30
    }
    $result.CSV.Count
}

Expected count = 4096 Returns: 4095 4092 4095 4096 4095 4096 4096 4096 4094 4092 ...

Going to -ThrottleLimit 1 seems to "solve" it, however I am losing the advantage of Parallel

Using following PS (core) version:

PSVersion                      7.3.6
PSEdition                      Core
GitCommitId                    7.3.6
OS                             Microsoft Windows 10.0.19044
Platform                       Win32NT
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0…}
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1
WSManStackVersion              3.0

Solution

  • Arrays are not inherintly thread-safe, so doing multiple $array += ... operations concurrently might result in race conditions where two updates overlap, hence the missing items in the final array.

    Use a thread-safe collection type instead, like a ConcurrentQueue for example:

    $contacts = 200001..200064
    while(1){
        $result = @{CSV=[System.Collections.Concurrent.ConcurrentQueue[psobject]]::new()};
        foreach($contact in $contacts){
            $contacts | ForEach-Object -Parallel {
                [void]($using:result).CSV.TryAdd("$($using:contact);$_")
            } -ThrottleLimit 30
        }
        $result.CSV.Count
    }