Link to home
Start Free TrialLog in
Avatar of jerryleeclark
jerryleeclarkFlag for United States of America

asked on

C# Background Worker general help

Hello all and thanks ahead for any help. I am trying to get my mind around how the BackgroundWorker works. I have tried various itteration of code and the following example I have developed is the closest I have come to working like I want it. This example I have come up with pretends that there are 4 temperature samplings each from Mars and Earth. There are two radiobuttons and a richtextbox on the form, if the user wants to get constant updated temperatures on Earth, they click the Earth radiobutton, likewise for Mars. There are subtle differences in the way I have implemented the worker here than the way MSDN does but I have tried the MSDN way and have the same problem.  In the radioButtonEARTH and MARS methods, I check for the bgw(BackGroundWorker) is busy, if so, cancel then start a new bgw. As posted here, I have a MessageBox and Sleep thread. This way seems to work...well mostly the UI the way it responds seems a little buggy but eventually will the return the temperatures on a 5 second interval as intended. If instead of a MessageBox/Thread.Sleep I use the while(bgw.IsBusy) the application hangs as if the bgw is always busy. However, if you uncomment out the MessageBox in the RunWorkerCompleted you will see that when the bgwTEMPERATURECHECKER.CancelAsync(); is called the bgw is completed, but for some reason stays busy. I hope the code is self explainatory but I will answer any questions about what I am trying to do. I would seem that some sort of GUI call has to be made to the main thread (the MessageBox call) in order for the bgw to reset. But this is just my inexperienced observation. Any help would be appreciated!! Thanks
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Threading;
 
namespace bgwexample
{
    public partial class Form1 : Form
    {
        public List<string> temperatures = new List<string>(1);
 
        public Form1()
        {
            InitializeComponent();
            InitializeBackgroudWorker();           
        }        
        private void InitializeBackgroudWorker()
        {
            bgwTEMPERATURECHECKER.DoWork += 
                new System.ComponentModel.DoWorkEventHandler(this.bgwTEMPERATURECHECKER_DoWork);
            bgwTEMPERATURECHECKER.ProgressChanged +=
                new ProgressChangedEventHandler(bgwTEMPERATURECHECKER_ProgressChanged);
            bgwTEMPERATURECHECKER.RunWorkerCompleted +=
                new RunWorkerCompletedEventHandler(bgwTEMPERATURECHECKER_RunWorkerCompleted);
        } 
        private void bgwTEMPERATURECHECKER_DoWork(
            object sender, DoWorkEventArgs e)
        {
            int i = 0;
            bool continuegettingtemperatures = true;            
 
            while (continuegettingtemperatures)
            {
                if (bgwTEMPERATURECHECKER.CancellationPending)
                {
                    e.Cancel = true;
                    continuegettingtemperatures = false;
                }
                else
                {
                    if (i == 0)
                    {
                        bgwTEMPERATURECHECKER.ReportProgress(1);
                        getTemperatures((string)e.Argument);
                        bgwTEMPERATURECHECKER.ReportProgress(100);
                        i++;
                    }
                    else if (i == 50) i = 0;
                    else i++;                    
                }                                  
                Thread.Sleep(100);
            }
        }
        private void getTemperatures(string earthORmars)
        {
            Random rand = new Random();
            if (earthORmars == "earth")
            {
                temperatures.Add(rand.Next(100).ToString() + "F");
                temperatures.Add(rand.Next(100).ToString() + "F");
                temperatures.Add(rand.Next(100).ToString() + "F");
                temperatures.Add(rand.Next(100).ToString() + "F");
            }
            else if (earthORmars == "mars")
            {
                temperatures.Add(rand.Next(100).ToString() + "C");
                temperatures.Add(rand.Next(100).ToString() + "C");
                temperatures.Add(rand.Next(100).ToString() + "C");
                temperatures.Add(rand.Next(100).ToString() + "C");
            }
            else
            {
                temperatures.Add("0");
                temperatures.Add("0");
                temperatures.Add("0");
                temperatures.Add("0");
            }
        }
        private void bgwTEMPERATURECHECKER_ProgressChanged(
            object sender, ProgressChangedEventArgs e)
        {
            if (e.ProgressPercentage == 1)
            {
                temperatures.Clear();
                richTextBoxTEMPERATURES.Clear();
            }
            else if (e.ProgressPercentage == 100)
            {
                foreach (string x in temperatures)
                    richTextBoxTEMPERATURES.AppendText(x + "\n");
            }
            else
            {
            }
        }
        private void bgwTEMPERATURECHECKER_RunWorkerCompleted(
            object sender, RunWorkerCompletedEventArgs e)
        {
            //MessageBox.Show("bgwTEMPERATURECHECKER_RunWorkerCompleted");
        } 
        private void radioButtonEARTH_Click(object sender, EventArgs e)
        {
            radioButtonEARTH.Checked = true;
            radioButtonMARS.Checked = false;
            
            if (bgwTEMPERATURECHECKER.IsBusy)
            {
                bgwTEMPERATURECHECKER.CancelAsync();
                MessageBox.Show("Switching to Earth temperatures");
                Thread.Sleep(1000);
                //while (bgwTEMPERATURECHECKER.IsBusy) Thread.Sleep(1000);
            }
            bgwTEMPERATURECHECKER.RunWorkerAsync("earth");
        }
        private void radioButtonMARS_Click(object sender, EventArgs e)
        {
            radioButtonEARTH.Checked = false;
            radioButtonMARS.Checked = true;
            
            if (bgwTEMPERATURECHECKER.IsBusy)
            {
                bgwTEMPERATURECHECKER.CancelAsync();
                MessageBox.Show("Switching to Mars temperatures");
                Thread.Sleep(1000);
                //while (bgwTEMPERATURECHECKER.IsBusy) Thread.Sleep(1000);
            }
            bgwTEMPERATURECHECKER.RunWorkerAsync("mars");
        }
 
 
    }//end public partial class Form1 : Form
}//end namespace bgwexample

Open in new window

bgwexample.txt
Avatar of p_davis
p_davis

it may not be the best way but i was having trouble with the isbusy flag as well. i put in an Application.DoEvents(); and it registers as it should.

maybe try:
private void radioButtonEARTH_Click(object sender, EventArgs e)
        {
            radioButtonEARTH.Checked = true;
            radioButtonMARS.Checked = false;
            
            Application.DoEvents(); //<-- modified code
 
            if (bgwTEMPERATURECHECKER.IsBusy)
            {
                bgwTEMPERATURECHECKER.CancelAsync();
                MessageBox.Show("Switching to Earth temperatures");
                //Thread.Sleep(1000); <--modified code
                //while (bgwTEMPERATURECHECKER.IsBusy) Thread.Sleep(1000);
            }
            bgwTEMPERATURECHECKER.RunWorkerAsync("earth");
        }

Open in new window

ASKER CERTIFIED SOLUTION
Avatar of mastoo
mastoo
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of jerryleeclark

ASKER

mastoo,
Hum,  I thought that the basic idea here was to have the second thread update the graphical elements (textboxes labels etc) as it sees fit and that can be accomplished in either the  "_DoWork", "_RunWorkerCompleted" OR  the  "_ProgressChanged" methods and that would be thread safe. It would seem at the very least changing those elements in the  "_ProgressChanged"  method should be safe. I clear the box in the  "_ProgressChanged" method so I am confused at how you think it is not thread safe.
On my reference to the MSDN site, what I mean is for example in the DoWork method one of the first things they do is declare a
BackgroundWorker worker = sender as BackgroudWorker;
and any progress changes or calls to the bgw thread seems to happen via the "worker" instead of the global bgwTEMPERATURECHECKER. When working within one self, maybe using "worker" makes sense, but like I said, setting up this example my way or MSDN way has the same trouble.

I understand now your explaination and I think I have figured out how to solve my problem but I ended up solving it with two richtextboxes on top of each other. Instead of the radiobuttons being the pusher of the thread, I decided to create two threads, one for earth and one for mars. they just sit happily in the background doing what they do and if the user clicks the earth or mars radiobuttons then all the code there does is show or hide the richtextbox. The two richtextboxes being richtextboxEARTH and richtextboxMARS  of course. They are positioned exactly on top of each other so clicking the radio buttons just show and bringtofront the one of interest and hide the other. Although this seems like cheeting in a strange way it makes more since. The real idea is that you want the program to always be aware of the temperatures in both places and the radiobuttons should not be the genesis for that, just that they change the view so you can see what the bgw has done.
Thanks, hope that helped.  My comment about not being thread-safe referred to the member variable:

public List<string> temperatures

It was being updated from both threads, possibly simultaneously.  One thread is adding while the other clears it.
YES, I figured that one out while changing to two different bgw's. Thank you I thought you were refering to the richtextbox, somewhere along the way dealing with two threads, it made sense to have two list, two richtextboxes etc. I am currious though, is it really a cheet to have to UI objects on top of each other?
Nope.  It might not be as "elegant" as some solutions but it's all about ease of understanding the code, so if it passes that test you're good.
Thanks so much for all your help, pressing on to Encrypted registry keys....dang!