Sharing of solutions for accessing controls across threads in C

  • 2020-05-26 09:58:26
  • OfStack

In principle, net prohibits cross-thread access to controls, as this can cause errors. The recommended solution is to use proxies, which indirectly manipulate controls that are not created by the same thread.

The second way is to prevent the compiler from checking cross-thread access, which can be done, but not guaranteed.

Recently, when I was working on a project, I encountered a cross-thread request to access the page control. However, I was always prompted with an error that I could not modify the value of the control of the thread that created the control in other threads.
First on the form, create an listbox,lable.


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 AccessControl
{
    public partial class Form1 : Form
    {      
        public Form1()
        {
            InitializeComponent();
        }
        private void Form1_Load(object sender, EventArgs e)
        {  
            Thread newthread = new Thread(new ThreadStart(BackgroundProcess));
            newthread.Start();         
        }
        /// <summary> 
        ///  define 1 A broker  
        /// </summary> 
        private delegate void CrossThreadOperationControl();
        private void BackgroundProcess()
        {
            //  Instantiate the proxy as 1 Three anonymous agents  
            CrossThreadOperationControl CrossDelete = delegate()          
            {            
                int i = 1;
                while (i<5)
                {
                   //  Add to the list box 1 A project  
                    listBox1.Items.Add("Item " + i.ToString());                   
                    i++;
                }
                label1.Text = " I'm accessing this in a new thread lable!";
                listBox1.Items.Add(label1.Text);
            }  ;
            listBox1.Invoke(CrossDelete);           
        }       
    }
}

I hope this tip will help you in your study and work. If there is a better way to access the control across threads, please share it with us.

C# cross-thread access control runtime error, can be resolved using MethodInvoker:

The original code:


private void btnOK_Click(object sender, EventArgs e)
        {
            tslInfo.Text = " Please wait for a while ...";

            Thread td = new Thread(new ThreadStart(run));
            td.Start();
        }

        /// <summary>
        ///  Thread method 
        /// </summary>
        private void run()
        {
            this.tslInfo.Text = " ready ";
        }

Revised:

private void btnOK_Click(object sender, EventArgs e)
        {
            tslInfo.Text = " Please wait for a while ...";

            Thread td = new Thread(new ThreadStart(threadRun));
            td.Start();
        }

        /// <summary>
        ///  Original thread method 
        /// </summary>
        private void run()
        {
            this.tslInfo.Text = " ready ";
        }
        /// <summary>
        ///  Thread method 
        /// </summary>
        private void threadRun()
        {
            MethodInvoker In = new MethodInvoker(run);
            this.BeginInvoke(In);
        }

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 do this in the traditional way, which I will describe 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 not valid:Control 'textBox1' accessed a Control a Control a Control Control Control Control Control Control Control Control 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 find that it is an static property, which means that wherever we modify the value in the project, it will act 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 option, which is to use 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 (recommended) :


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 the mountain 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=new FlushClient(ThreadFunction);
             FlushClient.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 is asynchronous with one asynchronous thread open):

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);
             FlushClient=new FlushClient(ThreadFunction);
             FlushClient.BeginInvoke(null,null);
        }
         private void ThreadFunction()
        {
              while (true)
            {
                this.textBox1.Text = DateTime.Now.ToString();
                Thread.Sleep(1000);
            }
        }
    }


Related articles: