2

I'm working with a WinForm from which all processes that I need are steered. Now I'm trying to integrate a BackgroundWorker with a ProgressBar and a cancellation button into my code. I want it to be locally around my code and not in a separate method. To test this, a new form is created with a progress bar (not active yet) and a button to stop a for-loop. However, the code is not working (and the progress bar is not even included yet). The form freezes immediately (see image) so I can't test the cancel button. The for-loop, however, is executed and "Done: " + l.ToString() is shown. How can I solve this?

void stopMeasurement(object sender, EventArgs e) 
{
    stopMeas = true;    
}

public void testcancel() // Test method which is triggered manually
{
    int l = 0;

    MetingProgress metingProgress = new MetingProgress();
    metingProgress.btnCancelmeting.Click += new EventHandler(stopMeasurement);

    BackgroundWorker worker = new BackgroundWorker();
    worker.WorkerSupportsCancellation = true;
    worker.DoWork += (sender, args) =>
    {                       
        for (int k = 0; k < 10; k++)
        {
            Thread.Sleep(1000);
            l++;

            if (worker.CancellationPending)
                break;
        }

        MessageBox.Show("Done: " + l.ToString());

    };
    worker.RunWorkerAsync(); 

    while (worker.IsBusy) 
    {
        if (stopMeas)
            worker.CancelAsync();
    }

    metingProgress.Dispose();
    MessageBox.Show("All done");

}

enter image description here

  • Yes, I already have my code for the several tasks that will need to go into the backgroundworker so I would like to have a setup like in my example. The for-loop resembles the tasks to be done. – 10a Aug 21 '17 at 7:04
  • ok I see, then testcancel is actually a method to simply trigger the thread and let it run in the background, and that's actually all to do. Since the thread will run for a longer time you should really not try to handle the dispose of the form in this method! it would be the job of the thread method. also the MessageBox.Show("All done"); line belongs into the DoWork event, since actually only the thread itself knows when the job is done, not the method that starts the thread – Mong Zhu Aug 21 '17 at 7:16
4

The form freezes immediately

this is because you have a while loop still running on the main thread! So the form will not be responsive. This is called buisy waiting. You will not be able to call the CancelAsync method.

One solution could be to remove the while-loop and place the cancel call into the button event code:

void stopMeasurement(object sender, EventArgs e)
{
    stopMeas = true;
    worker.CancelAsync();

}

What you have basically done is: you created a second cancelation token. So another possibility could be to use only stopMeas to cancel the background operation:

worker.DoWork += (sender, args) =>
{                       
    for (int k = 0; k < 10; k++)
    {
        Thread.Sleep(1000);
        l++;

        if (stopMeas)
            break;
    }

    string mes = stopMeas ? "Done: " + l.ToString() : "Task aborted!";
    MessageBox.Show(mes);

};

EDIT: also this line:

metingProgress.Dispose();

might lead to an ObjectDisposed exception. If the background process is still running and trying to update your progressbar and you already dispose the form. You should remove this line and leave it to the garbage collector.

  • Remove the loop? But then nothing is left inside the worker? – 10a Aug 21 '17 at 7:07
  • He is talking about the while(worker.IsBusy) loop. – Fildor Aug 21 '17 at 7:08
  • 2
    I still see a little problem here, @Tina: If you cancel, will the result still be treated like all the work had been done? – Fildor Aug 21 '17 at 7:15
  • good point, may be a return; statement without showing the message box would be good here, or even a boolean check at some point with a message that the task was canceled..?=! – Mong Zhu Aug 21 '17 at 7:18
  • 2
    I'm going to wrap my head around it and work out your suggestions. Thank you both for all your help. – 10a Aug 21 '17 at 7:25
4

This code is your problem:

while (worker.IsBusy) 
{
    if (stopMeas)
        worker.CancelAsync();
}

Your GUI-Thread is in that loop until your worker is done. You need to make your worker instance be reachable from within the EventHandler and call worker.CancelAsync() from there.


Outside this , I personally would improve the code in 2 steps:

  1. Move the whole BackgroundWorker into the MetingProgress class and make its constructor take a delegate for the actual work implementation.

  2. Use TAP (Task Async Pattern) , i.e. async/await Task with Progress and CancellationToken.

  • I'm trying to expand my code step by step and to understand how it works exactly, so probably I will end up with something you are suggesting. – 10a Aug 21 '17 at 7:15
  • 2
    The transition to TAP really needs some time to wrap your head around. Take your time but it's definitely worth trying. Doing it step by step is the best you can do to learn. Good luck to you! – Fildor Aug 21 '17 at 7:18

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.