Powershell: Question concerning foreach - parallel

Created on 29 Apr 2020  路  6Comments  路  Source: PowerShell/PowerShell

Hi there,

im really like the new foreach - parallel function but I need to be able to store the results in e.g. a global variable.

Look if you call a webservice in parallel, you need to process the result and save it to some e.g. global variable.

`
$items = $myGlobalVariable.Keys | Where-Object {$myGlobalVariable[$_].run -eq $false}

$items | ForEach-Object -parallel {
$response = Invoke-RestMethod xxxx
$myGlobalVariable[$_].ID = $response.ID
}
`

Any suggestions?

Issue-Enhancement Resolution-Answered

Most helpful comment

You can use a Dictionary, sure, but you can't use _that_ kind of dictionary due to the aforementioned threading issues. It also doesn't have a Synchronized() method like Hashtable does.

If you want a type-safe _and_ thread-safe dictionary, you'll need to look to System.Collections.Concurrent.ConcurrentDictionary.

As you'll see if you look through some of its methods, it has to be handled a good bit differently, with things like TryAdd() which _can_ fail and may need to be retried. It's a bit more complicated, but probably a lot more efficient to work with.

All 6 comments

You can reference items from the main runspace with $using:varName; $global: will not work as far as I'm aware. However, a majority of collections are not threadsafe, and you will encounter what can only be described as 'undefined behaviour' when trying to use them from more than one thread/runspace simultaneously as you are here.

You can prepare a hashtable for use in threaded operations using the static Synchronize method, like so:

$results = [hashtable]::Synchronized(@{ })

Which you can then safely use via $using:results in Foreach-Object -Parallel. There are other, more sophisticated collection types that can be used in a multithreaded context, but that's probably closest to what's familiar. Multithreading something correctly isn't easy, and the complexity of some of the threadsafe types reflects that.

Do note that $using:var doesn't permit assignment, or (I think?) indexing operations... so you may need to do something like $myResults = $using:Results at the start of the parallel scriptblock, and then reference the hashtable with that when adding entries.

Thanks already for your response, I will try this later today.

But one question: I have read some time ago that I should use System.Collections.Generic.Dictionary instead of an hashtable is this true? Can I use a dictonary instead of a hashtable or is this not possible in this case?

Thanks,

Alex

I have tested it with
$myResults = $using:Results
and it works all fine

Thanks

You can use a Dictionary, sure, but you can't use _that_ kind of dictionary due to the aforementioned threading issues. It also doesn't have a Synchronized() method like Hashtable does.

If you want a type-safe _and_ thread-safe dictionary, you'll need to look to System.Collections.Concurrent.ConcurrentDictionary.

As you'll see if you look through some of its methods, it has to be handled a good bit differently, with things like TryAdd() which _can_ fail and may need to be retried. It's a bit more complicated, but probably a lot more efficient to work with.

Thank you so much for your support

Was this page helpful?
0 / 5 - 0 ratings