Ask a questionAsk a question
 

AnswerUpdating a progress bar from inside a Parallel.For

  • Friday, February 06, 2009 7:40 PMGrahamBLG Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    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

  • Saturday, February 07, 2009 8:53 PMStephen Toub - MSFTMSFT, ModeratorUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     AnswerHas Code
    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++; });  
                });  
            });  
        }  

All Replies

  • Saturday, February 07, 2009 8:53 PMStephen Toub - MSFTMSFT, ModeratorUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     AnswerHas Code
    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++; });  
                });  
            });  
        }  

  • Monday, February 09, 2009 12:39 PMGrahamBLG Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    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
  • Saturday, October 10, 2009 11:02 AMJMN CandyBeat Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    Is there any better solution for doing that? I personally see it very confusing.
    From Spain
  • Sunday, October 11, 2009 2:34 AMStephen Toub - MSFTMSFT, ModeratorUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    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.
  • Monday, October 12, 2009 1:40 PMAndy Clymer Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    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.



  • Saturday, November 21, 2009 3:42 AMdodoshalac Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     Has Code
    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