I have a problem with the Parallel.ForEach
loop. It works as it should as long I don't Invoke
a method to increase the ProgressBar.Value
of the parent GUI program.
Where KeinPapierVersand
is a simple List<int>
object and EinzelnachweisDruckDatum
is a DateTime
.
Parallel.ForEach(KeinPapierVersand, partner =>
{
generate_PCL_nachweis(partner, EinzelnachweisDruckDatum, true, false);
generate_BGF_Report(partner, EinzelnachweisDruckDatum, false);
//If the following line is uncommented, the loop starts to idle after about 200
// processed Items and will never reach the code after the loop.
myProgressbar_einzelnachweis_druck.Parent.Invoke(new MethodInvoker(delegate
{
myProgressbar_einzelnachweis_druck.Value =
myProgressbar_einzelnachweis_druck.Value + 1;
}));
});
These are my first steps with parallelism in C#. I have no clue what's the problem here, there is no exception thrown (had it in a try/catch before).
Without the Invoke
of the ProgressBar
the loop ends always without a problem.
Why is my logic not working? Where is my error in reasoning? Please help.
Most likely, you're calling the Parallel.ForEach
method from your main UI thread. Like, in response to a button click or other UI event. Assuming this is the case, then Parallel.ForEach
line itself is running on the main UI thread and will block that main thread waiting till all the concurrent work that it's requesting is done.
The way that Invoke()
works is that it sends a message to the main UI thread and waits for the message to get processed. When that processing is complete, it will return and progress will continue. In your code, when a thread calls Invoke()
, it's going to wait there for Invoke
to complete. However, Invoke
never will complete because the main thread is blocked by the Parallel.ForEach
and can't process the request to update the progress bar till the Parallel.ForEach
is complete. It's a type of deadlock.
There are lots of ways to fix this problem. They all essentially amount to getting the Parallel.ForEach
off the main thread. One of the most simple ways to do this, if you can here (don't have enough of a code sample to tell) is to use async/await to kick off a task that then calls the Parallel.ForEach
and awaits the results. This will free up your main thread to be able to process the progress bar updates.
Once you get that working, you may also want to consider switching to using BeginInvoke
here instead of Invoke
because BeginInvoke
doesn't wait for the main thread to process the message before completing. It's a bit trickier to use because the update order isn't guaranteed and the update could even occur after the ForEach
completes. It won't fix your root problem, you have to fix that first, but it'll be more efficient because the Invoke()
version will actually slow down your Parallel
loop.