C#新开一个线程取到数据,如何更新到主线程UI上面

  • 问题
    之前有被面试官问过,在WinForm中,要去网络上获取数据,由于网络环境等原因,不能很快的完成,因此会发生进程阻塞,造成主进程假死的现象,需要怎么解决?

  • 思路
    因此,往往是新建一个线程,让他执行耗时的操作,主线程管理用户界面,不会出现UI假死的情况,但是通过线程获取到的数据如何更新回主进程的UI上呢?这是另外一个问题

  • 如下例子
    我们发现如果直接在线程里更新UI会报错,报“从不是创建控件lable1的线程访问它”,为什么会报这个错呢?这个问题就是跨线程访问控件问题,窗体上的控件只允许创建它们的线程访问,也就是主线程(UI线程),如果非主线程访问则会发生异常我们看到会报错,且这个错误是“从不是创建控件lable1的线程访问它”

C#新开一个线程取到数据,如何更新到主线程UI上面_第1张图片
C#新开一个线程取到数据,如何更新到主线程UI上面_第2张图片
TestClass.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace WindowsFormsApplication1
{
    public class TestClass
    {
        //声明一个delegate(委托)类型:TestDelegate,该类型可以搭载返回值为空,参数只有一个(long型)的方法。 
        public delegate void TestDelegate(long i);

        //声明一个TestDelegate类型的对象。该对象代表了返回值为空,参数只有一个(long型)的方法。它可以搭载N个方法。 
        public TestDelegate mainThread;

        /// 测试方法 
        ///           
        ///  
        public void TestFunction()
        {
            long i = 0;
            while (true)
            {
                i++;
                mainThread(i); //调用委托对象 
                Thread.Sleep(1000);  //线程等待1000毫秒 
            }
        }
    }
}

Program.cs

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            //创建TestClass类的对象 
            TestClass testClass = new TestClass();

            //在testclass对象的mainThread(委托)对象上搭载两个方法,在线程中调用mainThread对象时相当于调用了这两个方法。 
            testClass.mainThread = new TestClass.TestDelegate(RefreshLabMessage1);
            testClass.mainThread += new TestClass.TestDelegate(RefreshLabMessage2);

            //创建一个无参数的线程,这个线程执行TestClass类中的TestFunction方法。 
            Thread testClassThread = new Thread(new ThreadStart(testClass.TestFunction));
            //启动线程,启动之后线程才开始执行 
            testClassThread.Start();
        }

        ///  
        /// 在界面上更新线程执行次数 
        ///  
        ///  
        private void RefreshLabMessage1(long i)
        {
            //判断该方法是否被主线程调用,也就是创建labMessage1控件的线程,当控件的InvokeRequired属性为ture时,说明是被主线程以外的线程调用。如果不加判断,会造成异常 
            if (this.labMessage1.InvokeRequired)
            {
                //再次创建一个TestClass类的对象 
                TestClass testclass = new TestClass();
                //为新对象的mainThread对象搭载方法 
                testclass.mainThread = new TestClass.TestDelegate(RefreshLabMessage1);
                //this指窗体,在这调用窗体的Invoke方法,也就是用窗体的创建线程来执行mainThread对象委托的方法,再加上需要的参数(i) 
                this.Invoke(testclass.mainThread, new object[] { i });
            }
            else
            {
                labMessage1.Text = i.ToString();
            }
        }


        ///  
        /// 在界面上更新线程执行次数 
        ///  
        ///  
        private void RefreshLabMessage2(long i)
        {
            //同上 
            if (this.labMessage2.InvokeRequired)
            {
                //再次创建一个TestClass类的对象 
                TestClass testclass = new TestClass();
                //为新对象的mainThread对象搭载方法 
                testclass.mainThread = new TestClass.TestDelegate(RefreshLabMessage2);
                //this指窗体,在这调用窗体的Invoke方法,也就是用窗体的创建线程来执行mainThread对象委托的方法,再加上需要的参数(i) 
                this.Invoke(testclass.mainThread, new object[] { i });
            }
            else
            {
                labMessage2.Text = i.ToString();
            }
        }
    }
}

执行效果
C#新开一个线程取到数据,如何更新到主线程UI上面_第3张图片
C#新开一个线程取到数据,如何更新到主线程UI上面_第4张图片

你可能感兴趣的:(C#中的委托)