Categories
c# invoke multithreading winforms

Cross-thread operation not valid: Control accessed from a thread other than the thread it was created on

657

I have a scenario. (Windows Forms, C#, .NET)

  1. There is a main form which hosts some user control.
  2. The user control does some heavy data operation, such that if I directly call the UserControl_Load method the UI become nonresponsive for the duration for load method execution.
  3. To overcome this I load data on different thread (trying to change existing code as little as I can)
  4. I used a background worker thread which will be loading the data and when done will notify the application that it has done its work.
  5. Now came a real problem. All the UI (main form and its child usercontrols) was created on the primary main thread. In the LOAD method of the usercontrol I’m fetching data based on the values of some control (like textbox) on userControl.

The pseudocode would look like this:

CODE 1

UserContrl1_LoadDataMethod()
{
    if (textbox1.text == "MyName") // This gives exception
    {
        //Load data corresponding to "MyName".
        //Populate a globale variable List<string> which will be binded to grid at some later stage.
    }
}

The Exception it gave was

Cross-thread operation not valid: Control accessed from a thread other than the thread it was created on.

To know more about this I did some googling and a suggestion came up like using the following code

CODE 2

UserContrl1_LoadDataMethod()
{
    if (InvokeRequired) // Line #1
    {
        this.Invoke(new MethodInvoker(UserContrl1_LoadDataMethod));
        return;
    }

    if (textbox1.text == "MyName") // Now it won't give an exception
    {
    //Load data correspondin to "MyName"
        //Populate a globale variable List<string> which will be binded to grid at some later stage
    }
}

But it still seems that I’ve come back to square one. The Application again
becomes unresponsive. It seems to be due to the execution of line #1 if condition. The loading task is again done by the parent thread and not the third that I spawned.

I don’t know whether I perceived this right or wrong. I’m new to threading.

How do I resolve this and also what is the effect of execution of Line#1 if block?

The situation is this: I want to load data into a global variable based on the value of a control. I don’t want to change the value of a control from the child thread. I’m not going to do it ever from a child thread.

So only accessing the value so that the corresponding data can be fetched from the database.

1

  • For my particular instance of this error, I found the workaround to be to use a BackgroundWorker on the form to handle the data-intensive parts of the code. (i.e. put all of the problem code into the backgroundWorker1_DoWork() method and call it via backgroundWorker1.RunWorkerAsync())… These two sources pointed me in the correct direction: stackoverflow.com/questions/4806742/… youtube.com/watch?v=MLrrbG6V1zM

    – Giollia

    Sep 18, 2019 at 19:12

472

As per Prerak K’s update comment (since deleted):

I guess I have not presented the question properly.

Situation is this: I want to load data into a global variable based on the value of a control. I don’t want to change the value of a control from the child thread. I’m not going to do it ever from a child thread.

So only accessing the value so that corresponding data can be fetched from the database.

The solution you want then should look like:

UserContrl1_LOadDataMethod()
{
    string name = "";
    if(textbox1.InvokeRequired)
    {
        textbox1.Invoke(new MethodInvoker(delegate { name = textbox1.text; }));
    }
    if(name == "MyName")
    {
        // do whatever
    }
}

Do your serious processing in the separate thread before you attempt to switch back to the control’s thread. For example:

UserContrl1_LOadDataMethod()
{
    if(textbox1.text=="MyName") //<<======Now it wont give exception**
    {
        //Load data correspondin to "MyName"
        //Populate a globale variable List<string> which will be
        //bound to grid at some later stage
        if(InvokeRequired)
        {
            // after we've done all the processing, 
            this.Invoke(new MethodInvoker(delegate {
                // load the control with the appropriate data
            }));
            return;
        }
    }
}

2

  • 1

    It’s been a while now since I’ve done C# programming, but based on the MSDN article and my patchy knowledge, it looks like it.

    Dec 13, 2016 at 21:17

  • 1

    The difference is, BeginInvoke() is asynchronous while Invoke() runs synchronously. stackoverflow.com/questions/229554/…

    – frzsombor

    Oct 13, 2017 at 0:18


212

Threading Model in UI

Please read the Threading Model in UI applications (old VB link is here) in order to understand basic concepts. The link navigates to page that describes the WPF threading model. However, Windows Forms utilizes the same idea.

The UI Thread

  • There is only one thread (UI thread), that is allowed to access System.Windows.Forms.Control and its subclasses members.
  • Attempt to access member of System.Windows.Forms.Control from different thread than UI thread will cause cross-thread exception.
  • Since there is only one thread, all UI operations are queued as work items into that thread:

enter image description here

enter image description here

BeginInvoke and Invoke methods

enter image description here

Invoke

enter image description here

BeginInvoke

enter image description here

Code solution

Read answers on question How to update the GUI from another thread in C#?.
For C# 5.0 and .NET 4.5 the recommended solution is here.

1

75

You only want to use Invoke or BeginInvoke for the bare minimum piece of work required to change the UI. Your “heavy” method should execute on another thread (e.g. via BackgroundWorker) but then using Control.Invoke/Control.BeginInvoke just to update the UI. That way your UI thread will be free to handle UI events etc.

See my threading article for a WinForms example – although the article was written before BackgroundWorker arrived on the scene, and I’m afraid I haven’t updated it in that respect. BackgroundWorker merely simplifies the callback a bit.

4

  • here in this condition of mine . i m not even changing the UI. I m just accessig its current values from the child thread. any suggestion hw to implement

    – Prerak K

    Sep 26, 2008 at 21:26

  • 1

    You still need to marshal over to the UI thread even just to access properties. If your method can’t continue until the value is accessed, you can use a delegate which returns the value. But yes, go via the UI thread.

    – Jon Skeet

    Sep 26, 2008 at 21:38

  • Hi Jon, i belive you are heading me to the right direction. Yes i need the value without it i cant proceed further. Please could you eloborate on that ‘ Using a delegate which return a value’. Thanks

    – Prerak K

    Sep 26, 2008 at 21:46

  • 1

    Use a delegate such as Func<string>: string text = textbox1.Invoke((Func<string>) () => textbox1.Text); (That’s assuming you’re using C# 3.0 – you could use an anonymous method otherwise.)

    – Jon Skeet

    Sep 26, 2008 at 21:49