Parallel Computing Developer Center >
Parallel Computing Forums
>
Parallel Extensions to the .NET Framework
>
Updating a progress bar from inside a Parallel.For
Updating a progress bar from inside a Parallel.For
- Hi,
I'm using the CTP for .NET 3.5 in VS2008
I have 2 problems.
1/
I have a custom Control with a ProgressBar and a processing Engine object.
At the heart of the Engine object is a Parellel.For 'loop'.
I wish to step the ProgressBar from within the loop and am failing to do so usefully.
I first tried firing an event from inside the loop which was received by the control which then updated the progressbar ( see code below). This failed to return and the whole loop hung.
private delegate void ThreadSafedelegate();
ThreadSafedelegate update = delegate()
{
progressBar.Value++;
Application.DoEvents();
};
if (InvokeRequired)
Invoke(update);
else
update();
System.Diagnostics.Trace.WriteLine("After update"); // this line never reached
Using Parallel.Invoke(Step) fared no better.
I then used Task.Create(() => Step(), TaskCreationOptions.Detached) to fire the event. This rather wonderfully ran all the processing loops and only afterwards fired all the step events!
I've considered passing the control to the engine and then calling the control step function directly, but I can't see this helping as the problem appears to be in dealing with the control UI than with firing the event.
Any suggestions about how I could go about this?
2/ in a similar situation to the above I have an abort button on the control and I use that to set a flag which is examined in the processing engine Parallel.For 'loop' to call state.Stop(). All well and good, but when the loop is running no button clicks are processed by the form. When I insert an Application.DoEvents() in the loop, the behaviour doesn't change except that the engine eventually crashes with the following message:-
System.InvalidOperationException was unhandled
Message="The Undo operation encountered a context that is different from what was applied in the corresponding Set operation. The possible cause is that a context was Set on the thread and not reverted(undone)."
Source="mscorlib"
StackTrace:
at System.Threading.SynchronizationContextSwitcher.Undo()
at System.Threading.ExecutionContextSwitcher.Undo()
at System.Threading.ExecutionContext.runFinallyCode(Object userData, Boolean exceptionThrown)
at System.Runtime.CompilerServices.RuntimeHelpers.ExecuteBackoutCodeHelper(Object backoutCode, Object userData, Boolean exceptionThrown)
at System.Runtime.CompilerServices.RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup(TryCode code, CleanupCode backoutCode, Object userData)
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.Tasks.Task.Execute(Boolean stolen)
at System.Threading.Tasks.TaskBase.InvokeStolen()
at System.Threading.ConcurrencyScheduler.InternalContext.Dispatch()
at System.Threading.ConcurrencyScheduler.ThreadInternalContext.ThreadStartBridge(IntPtr dummy)
InnerException:
Any suggestions?
Cheers
Graham
Answers
- Hi Graham-
Here's a simple example of updating a progress bar from a parallel loop. Depending on your needs, the relevant code might change, but I hope this provides at least some guidance:
using System; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; class SimpleProgressBar : Form { [STAThread] static void Main(string[] args) { Application.EnableVisualStyles(); Application.Run(new SimpleProgressBar()); } protected override void OnLoad(EventArgs e) { base.OnLoad(e); int iterations = 100; ProgressBar pb = new ProgressBar(); pb.Maximum = iterations; pb.Dock = DockStyle.Fill; Controls.Add(pb); Task.Create(delegate { Parallel.For(0, iterations, i => { Thread.SpinWait(50000000); // do work here BeginInvoke((Action)delegate { pb.Value++; }); }); }); } } - Proposed As Answer byStephen Toub - MSFTMSFT, ModeratorSaturday, February 07, 2009 8:54 PM
- Marked As Answer byStephen Toub - MSFTMSFT, ModeratorSunday, February 08, 2009 10:30 PM
All Replies
- Hi Graham-
Here's a simple example of updating a progress bar from a parallel loop. Depending on your needs, the relevant code might change, but I hope this provides at least some guidance:
using System; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; class SimpleProgressBar : Form { [STAThread] static void Main(string[] args) { Application.EnableVisualStyles(); Application.Run(new SimpleProgressBar()); } protected override void OnLoad(EventArgs e) { base.OnLoad(e); int iterations = 100; ProgressBar pb = new ProgressBar(); pb.Maximum = iterations; pb.Dock = DockStyle.Fill; Controls.Add(pb); Task.Create(delegate { Parallel.For(0, iterations, i => { Thread.SpinWait(50000000); // do work here BeginInvoke((Action)delegate { pb.Value++; }); }); }); } } - Proposed As Answer byStephen Toub - MSFTMSFT, ModeratorSaturday, February 07, 2009 8:54 PM
- Marked As Answer byStephen Toub - MSFTMSFT, ModeratorSunday, February 08, 2009 10:30 PM
- Hi Stephen,
Thanks for your reply. With a bit of tweaking I was able to get the progress bar working.
Because, my engine/loop object was not part of the control object I could not use BeginInvoke directly. However, this was easily worked around using an event.
I have essentially created the engine from inside a Task.Create ( delegate { create and run engine from here}); and this seems to also solve the second of my issues though I am confused as to why.
I guess it is time to delve back how GUI threads work.
Thanks again
Graham
- Is there any better solution for doing that? I personally see it very confusing.
From Spain - If all you want to do is offload some work from the UI, do some long background operation, have intermittent progress updates, and be notified when the operation has completed, the BackgroundWorker class is your friend... that's it's purpose in life.
- A while back I recorded a screen cast that covers how to marshal work between non ui threads back onto UI threads utilising the new goodness in .NET 4.
- Are there any examples for something similar in June CTP for .NET 3.5 in VS2008 but using VB.Net? I would like to update a label on a form at each iteration of the Parallel.For, something like this:
Public Sub Main() 'crunch numbers in parallel lblStatus.Txt = "Starting...." Parallel.For(1, 2000, AddressOf Crunch) End Sub Public Sub Crunch(ByVal i As Integer) '... crunch a bunch of numbers here ' update label now that we are done crunching lblStatus.Txt = "Just finished iteration #" + i.ToString End Sub


