Summary of several problems with multithreaded access to the winform control in c

  • 2020-05-24 06:03:18
  • OfStack

When we do winform applications, most of the time we encounter the problem of using control information on the multithreaded control interface. However, we can't solve this problem in the traditional way, which I will discuss in detail below.

First, the traditional method:


public partial class Form1 : Form
     {
        public Form1()
        {
            InitializeComponent();
        }
        private void Form1_Load(object sender, EventArgs e)
        {
            Thread thread = new Thread(ThreadFuntion);
            thread.IsBackground = true;
            thread.Start();
        }
        private void ThreadFuntion()
        {
            while (true)
            {
                this.textBox1.Text = DateTime.Now.ToString();
                Thread.Sleep(1000);
            }
        }
    }

Running this code, we see that the system throws an exception: Cross-thread operation not valid:Control 'textBox1' accessed from thread other than than it created on. This is because net 2.0 has enhanced security and does not allow direct cross-thread access to control properties in winform. So how to solve this problem, here are a few options.

In the first scenario, we add a line of code in the Form1_Load() method:


private void Form1_Load(object sender, EventArgs e)
      {
            Control.CheckForIllegalCrossThreadCalls = false;
            Thread thread = new Thread(ThreadFuntion);
            thread.IsBackground = true;
            thread.Start();
      }

After adding this code, the program can be found to work normally. This code means that in this class we do not check whether the call across the thread is legal (if it runs without the exception, then the system and the default is not checked). However, this method is not advisable. If we look at the definition of the CheckForIllegalCrossThreadCalls property, we will see that it is an static property, which means that no matter where we modify the value in the project, it will take effect globally. And if there is an exception to a cross-thread access like this, we usually check for it. If someone else in the project modifies this property, then our scheme fails and we have to adopt another scheme.

Let's look at the second scenario, which USES delegate and invoke to control control information from other threads. A lot of people have written about this kind of control on the Internet. However, I have read a lot of these posts, and it seems that there is no problem, but actually it doesn't solve this problem. First, let's look at the imperfect way on the Internet:


public partial class Form1 : Form
    {
        private delegate void FlushClient();// The agent 
        public Form1()
        {
            InitializeComponent();
        }
        private void Form1_Load(object sender, EventArgs e)
        {
            Thread thread = new Thread(CrossThreadFlush);
            thread.IsBackground=true;
            thread.Start();
        }

        private void CrossThreadFlush()
        {
            // Bind the proxy to the method  
            FlushClient fc = new FlushClient(ThreadFuntion);
            this.BeginInvoke(fc);// Call the agent 
        }
        private void ThreadFuntion()
        {
            while (true)
            {
                this.textBox1.Text = DateTime.Now.ToString();
                Thread.Sleep(1000);
            }
        }
    }

In this way we can see that the exceptions accessed across threads are gone. But a new problem has emerged and the interface is not responding. Why do we have this problem? We just let the new thread refresh in an infinite loop, which theoretically should not affect the main thread. In fact, this is equivalent to "injecting" the newly opened thread into the main thread of control, which takes control of the main thread. As long as the thread does not return, the main thread will never respond. Even if the new thread does not use an infinite loop, it can return. The use of multithreading in this way also loses its original meaning.

Now let's look at the recommended solution:


public partial class Form1 : Form
    {
        private delegate void FlushClient();// The agent 
        public Form1()
        {
            InitializeComponent();
        }
        private void Form1_Load(object sender, EventArgs e)
        {
            Thread thread = new Thread(CrossThreadFlush);
            thread.IsBackground = true;
            thread.Start();
        }

        private void CrossThreadFlush()
        {
            while (true)
            {
                // will sleep And an infinite loop are placed outside waiting for asynchrony 
                Thread.Sleep(1000);
                ThreadFunction();
            }
        }
        private void ThreadFunction()
        {
            if (this.textBox1.InvokeRequired)// Wait for the asynchronous 
            {
                FlushClient fc = new FlushClient(ThreadFunction);
                this.Invoke(fc);// The refresh method is called through the proxy 
            }
            else
            {
                this.textBox1.Text = DateTime.Now.ToString();
            }
        }
    }

By running the above code, we can see that the problem has been solved, and by waiting for asynchrony, we won't always have control of the main thread, so we can complete multithreaded control of the winform multithreaded control without the occurrence of a cross-thread call exception.

I recently found an even better solution to the problem posed by deep forest, using the asynchronous invocation of delegate. You can check it out:


public partial class Form1 : Form
    {
        private delegate void FlushClient();// The agent 
        public Form1()
        {
            InitializeComponent();
        }
        private void Form1_Load(object sender, EventArgs e)
        {
            Thread thread = new Thread(CrossThreadFlush);
            thread.IsBackground = true;
            thread.Start();
        }

        private void CrossThreadFlush()
        {
             FlushClient fc=new FlushClient(ThreadFunction);
             fc.BeginInvoke(null,null);
        }
        private void ThreadFunction()
        {
              while (true)
            {
                this.textBox1.Text = DateTime.Now.ToString();
                Thread.Sleep(1000);
            }
        }
    }

This method can also be simplified to (because delegate's asynchrony is an asynchronous thread):


public partial class Form1 : Form
    {
        private delegate void FlushClient();// The agent 
        public Form1()
        {
            InitializeComponent();
        }
        private void Form1_Load(object sender, EventArgs e)
        {
             FlushClient fc=new FlushClient(ThreadFunction); 
             fc.BeginInvoke(null,null);
        }
         private void ThreadFunction()
        {
              while (true)
            {
                this.textBox1.Text = DateTime.Now.ToString();
                Thread.Sleep(1000);
            }
        }
    }

The poor monk gives feedback with rejoice

while (true) and Thread Sleep (1000); Don't put it in the invoke to control it


Related articles: