winforms - while - radmessagebox




In WinForms, why can't you update UI controls from other threads? (8)

I think this is a brilliant question - and I think there is need of a better answer.

Surely the only reason is that there is something in a framework somewhere that isn't very thread-safe.

That "something" is almost every single instance member on every single control in System.Windows.Forms.

The MSDN documentation for many controls in System.Windows.Forms, if not all of them, say "Any public static (Shared in Visual Basic) members of this type are thread safe. Any instance members are not guaranteed to be thread safe."

This means that instance members such as TextBox.Text {get; set;} are not reentrant.

Making each of those instance members thread safe could introduce a lot of overhead that most applications do not need. Instead the designers of the .Net framework decided, and I think correctly, that the burden of synchronizing access to forms controls from multiple threads should be put on the programmer.

[Edit]

Although this question only asks "why" here is a link to an article that explains "how":

How to: Make Thread-Safe Calls to Windows Forms Controls on MSDN

http://msdn.microsoft.com/en-us/library/ms171728.aspx

I'm sure there is a good (or at least decent) reason for this. What is it?


BackgroundWorker seems to be best choice for you.

Here is my minimal example. After you click on the button the background worker will begin working in background thread and also report its progress simultaneously. It will also report after the work completes.

using System.ComponentModel;
...
    private void button1_Click(object sender, EventArgs e)
    {
        BackgroundWorker bw = new BackgroundWorker();

        // this allows our worker to report progress during work
        bw.WorkerReportsProgress = true;

        // what to do in the background thread
        bw.DoWork += new DoWorkEventHandler(
        delegate(object o, DoWorkEventArgs args)
        {
            BackgroundWorker b = o as BackgroundWorker;

            // do some simple processing for 10 seconds
            for (int i = 1; i <= 10; i++)
            {
                // report the progress in percent
                b.ReportProgress(i * 10);
                Thread.Sleep(1000);
            }

        });

        // what to do when progress changed (update the progress bar for example)
        bw.ProgressChanged += new ProgressChangedEventHandler(
        delegate(object o, ProgressChangedEventArgs args)
        {
            label1.Text = string.Format("{0}% Completed", args.ProgressPercentage);
        });

        // what to do when worker completes its task (notify the user)
        bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(
        delegate(object o, RunWorkerCompletedEventArgs args)
        {
            label1.Text = "Finished!";
        });

        bw.RunWorkerAsync();
    }

Note:

  • I put everything in single method using C#'s anonymous method for simplicity but you can always pull them out to different methods.
  • It is safe to update GUI within ProgressChanged or RunWorkerCompleted handlers. However, updating GUI from DoWork will cause InvalidOperationException.

As others have stated, you cannot update your UI from any thread other than the one it was created by.

If a thread wants to update the UI, it needs to invoke a method on the UI control on the same thread that created it using BeginInvoke.

PS: I am assuming when you say UI, you mean a WindowsForms and not WPF. I have used the above solution successfully in WinForms.

Update: Here are a couple of articles that explain the concept in-depth: * Threading in Windows Forms * WinForms UI Thread Invokes: An In-Depth Review of Invoke/BeginInvoke/InvokeRequred

Also, related question from SO: In WinForms, why can't you update UI controls from other threads?


Because you can easily end up with a deadlock (among other issues).

For exmaple, your secondary thread could be trying to update the UI control, but the UI control will be waiting for a resource locked by the secondary thread to be released, so both threads end up waiting for each other to finish. As others have commented this situation is not unique to UI code, but is particularly common.

In other languages such as C++ you are free to try and do this (without an exception being thrown as in WinForms), but your application may freeze and stop responding should a deadlock occur.

Incidentally, you can easily tell the UI thread that you want to update a control, just create a delegate, then call the (asynchronous) BeginInvoke method on that control passing it your delegate. E.g.

myControl.BeginInvoke(myControl.UpdateFunction);

This is the equivalent to doing a C++/MFC PostMessage from a worker thread


The ThreadPool.QueueUserWorkItem is pretty ideal for something simple. The only caveat is accessing a control from the other thread.

System.Threading.ThreadPool.QueueUserWorkItem(delegate {
    DoSomethingThatDoesntInvolveAControl();
}, null);

you have 2 issues

a) you are not supposed to update the UI from other threads

b) even if you were allowed to do it you need to tell the UI to repaint

These are non trivial issues

If you are just experimenting with threading then use Debug.WriteLine to see what the background threads are doing

If you actually need to update the UI in the background then lookup BeginInvoke and InvokeNeeded


C#: Populating a UI using separate threads

Within your threads methods, such as DoWork() for the BackgroundWorker class, for example, you will need to instiate a delegate method to populate your UI control. Then, verifying whether your UI control requires to be invoked (InvokeRequired property), then invoking it when it'srequired to.

private delegate IList<MyObject> PopulateUiControl();

private void myThread_DoWork(object sender, DoWorkEventArgs e) {
    PopulateUiControl myDelegate = FillUiControl;

    while(uiControl.InvokeRequired)
        uiControl.Invoke(myDelegate);
}

private IList<MyObject> FillUiControl() {
    uiControl.Items = myThreadResultsITems;
}

It is not a precise working code, as I can't take the time to do the research, etc. but this shall put you in the path to succeed.

In the end, I agree with the others, try to avoid such things in the future, as it can get tricky to debug or reveal some strange behaviour. Perhaps .NET 4 has some improvements on the topic as Microsoft has worked hard to make parallelism easy for the use of multicore processors for developers.


How do I run a simple bit of code in a new thread?

Good place to start reading is Joe Albahari.

If you want to create your own thread, this is as simple as it gets:

using System.Threading;
new Thread(() => 
{
    Thread.CurrentThread.IsBackground = true; 
    /* run your code here */ 
    Console.WriteLine("Hello, world"); 
}).Start();






multithreading