0

It's a C# winform .Net framework 2.0 project: The time-consuming network transferring jobs run in a BackupgroundWorker. In this backgroundwork thread, SynchronizationContext method is used to send the current job title to a message dialog in the main UI thread. However, in my code, thread racing problem happens and "NullReferenceException" is given sometime (not always, but randomly). Here is my code:

private void DoBtn_Click(object sender, EventArgs e)
{
    m_msgForm = new MsgForm();       //m_msgForm is a member variable of the class, and MsgForm is a form class with a "public static SynchronizationContext synContext"
    m_msgForm.UpdateMsg("starting ...");   //UpdateMsg is public method to show progress information

BackgroundWorker myBackgroundWorker = new BackgroundWorker();
        myBackgroundWorker.DoWork +=new DoWorkEventHandler(myBackgroundWorker_DoWork);
        myBackgroundWorker.RunWorkerCompleted +=new RunWorkerCompletedEventHandler(myBackgroundWorker_RunWorkerCompleted);
        myBackgroundWorker.RunWorkerAsync(theBackgroundArgument);  

    m_msgForm.ShowDialog();    //show as a modal dialog
 }

And in the Background worker thread:

private void myBackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
   //... some code
   string msgText ="doing job: " +job.Title;
   RestoreMsgForm.synContext.Send(m_msgForm.UpdateMsg, msgText);    //send message to m_msgForm in the UI thread 

   //... some code
}

The problem is : sometime the "RestoreMsgForm.synContext.Send() in the backgroundworker thread will run before m_msgForm.ShowDialog() in the UI thead. And in this case, NullReferenceException will be throw.

How to solve this problem? thanks.

the definition of RestoreMsgForm is

  public partial class RestoreMsgForm : Form
    {
        public static SynchronizationContext synContext;
        public RestoreMsgForm()
        {
            InitializeComponent();
        }
        private void RestoreMsgForm_Load(object sender, EventArgs e)
        {
            synContext = SynchronizationContext.Current;
        }
        public void UpdateMsg(object msg)
        {
            msgLabel.Text = (string)msg;
        }
}
John Saunders
  • 160,644
  • 26
  • 247
  • 397
skyfree
  • 867
  • 2
  • 10
  • 29
  • What is RestoreMsgForm, how is it related to MsgForm, and what is the definition of RestoreMsgForm.synContext? – Ilya Kogan May 18 '13 at 03:47
  • the definition of RestoreMsgForm is: public partial class RestoreMsgForm : Form { public static SynchronizationContext synContext; public RestoreMsgForm() { InitializeComponent(); } private void RestoreMsgForm_Load(object sender, EventArgs e) { synContext = SynchronizationContext.Current; } public void UpdateMsg(object msg) { msgLabel.Text = (string)msg; } – skyfree May 18 '13 at 03:48
  • Almost all cases of `NullReferenceException` are the same. Please see "[What is a NullReferenceException in .NET?](http://stackoverflow.com/questions/4660142/what-is-a-nullreferenceexception-in-net)" for some hints. – John Saunders May 18 '13 at 03:58
  • I am thinking that I do not use/understand the SynchronizationContext concept well – skyfree May 18 '13 at 04:03
  • What's the best way to report the progress message to dialog in UI thread from a backgroundworker,then? – skyfree May 18 '13 at 04:07

1 Answers1

1

See if it works like this instead:

    private void DoBtn_Click(object sender, EventArgs e)
    {
        m_msgForm = new MsgForm();       //m_msgForm is a member variable of the class, and MsgForm is a form class with a "public static SynchronizationContext synContext"
        m_msgForm.UpdateMsg("starting ...");   //UpdateMsg is public method to show progress information

        BackgroundWorker myBackgroundWorker = new BackgroundWorker();
        myBackgroundWorker.DoWork += new DoWorkEventHandler(myBackgroundWorker_DoWork);
        myBackgroundWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(myBackgroundWorker_RunWorkerCompleted);

        myBackgroundWorker.WorkerReportsProgress = true;
        myBackgroundWorker.ProgressChanged += new ProgressChangedEventHandler(myBackgroundWorker_ProgressChanged);

        myBackgroundWorker.RunWorkerAsync(theBackgroundArgument);  
    }

    private void myBackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
    {
        BackgroundWorker worker = (BackgroundWorker)sender;
        worker.ReportProgress(-1);

        //... some code

        string msgText = "doing job: " + job.Title;
        worker.ReportProgress(0, msgText);

        //... some code

        worker.ReportProgress(0, "...other text...");

        //... some code

        worker.ReportProgress(0, "...etc...");
    }

    void myBackgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        if (e.ProgressPercentage == -1)
        {
            m_msgForm.ShowDialog();    //show as a modal dialog
        }
        else
        {
            m_msgForm.UpdateMsg(e.UserState.ToString);
        }
    }

*Note that we have to set .WorkerReportsProgress = true for the BackgroundWorker() so we can use ReportProgress() and receive the ProgressChanged() event.

Idle_Mind
  • 38,363
  • 3
  • 29
  • 40
  • Thank you Idle, I think this will work and I will try your code. My question: is this the typical way to update the UI progress message? – skyfree May 18 '13 at 04:30
  • I don't know where I get the SynchronizationContext method, seems it is weird and not working in some situation. – skyfree May 18 '13 at 04:32
  • SynchronizationContext method is fine. Just kinda weird to mix the BackgroundWorker() with it, as the BackgroundWorker() already has a built-in mechanism for this. I've typically seen the SynchronizationContext used in classes that are manually implementing threading and want to raise an event that is already marshaled to the main UI thread. One thing I've done here is cause the dialog to be shown from within the background thread thus ensuring it is displayed before we attempt to update it. The same kind of thing could be done with the SynchronizationContext. – Idle_Mind May 18 '13 at 05:23