Search code examples
powershellforeach-object

Powershell Foreach-Object -Parallel how to change the value of a variable outside the loop (track progress)


This code prints a simple progression, doing one thing at a time:

$files = 1..100
$i = 0
$files | Foreach-Object {
    $progress = ("#" * $i)
    Write-Host  "`r$progress" -NoNewLine
    $i ++
    Start-Sleep -s 0.1
}

But if I want to do two things in parallel at the same time, I can't output the progress because I can't change the variable outside the parallel loops. This doesn't do what's needed:

$files = 1..100
$i = 0
$files | Foreach-Object -ThrottleLimit 2 -Parallel {
    $progress = ("#" * $i)
    Write-Host  "`r$progress" -NoNewLine
    $i ++
    Start-Sleep -s 0.1
}

I can't find a good solution for accessing an external variable not only to read it with $Using, but also to change it. Is this even possible in Powershell 7?


Solution

  • I think this is right, based on Writing Progress across multiple threads with Foreach Parallel. It may be missing a lock, but just for writing progress it's probably not a big deal. In this case you can just use the filename for the progress too.

    $hash = @{i = 1}
    $sync = [System.Collections.Hashtable]::Synchronized($hash)
    
    $files = 1..100
    $files | Foreach-Object -throttlelimit 2 -parallel {
        $syncCopy = $using:sync
        $progress = '#' * $syncCopy.i
        #$progress = '#' * $_
        Write-Host  "`r$progress" -NoNewLine
        $syncCopy.i++
        Start-Sleep .1
    }
    

    Output:

    ####################################################################################################