• 多线程、委托、Invoke解决winform界面卡死的问题,并带开关


    一、知识点介绍
    1,更新控件的内容,应该调用控件的Invoke方法。
    Invoke指: 在拥有控件的基础窗口句柄的线程上,用指定的参数列表执行指定委托。该方法接收一个委托类型和委托的参数,因此需要定义委托类型变量,然后传递给Invoke方法。
     
    如果其他线程直接调用方法更新控件内容,报错:线程间操作无效: 从不是创建控件“richTextBox1”的线程访问它。
     
    2,委托的本质是某一类型的方法,这些方法具有相同的参数和返回类型。
    委托类似于C语言中的函数指针,可以指向多个相同类型的函数。
    定义委托,只需要在函数返回类型前加上delegate关键词,把函数体大括号{}的内容换成分号即可。比如:
    public delegate void DelegateFun(string msg);
    DelegateFun就代表了一个函数类型,它接收string参数,返回void。
     
    3,开辟一个线程,直接启动,后面通过挂起和唤醒实现暂停功能。
    Thread t = new Thread(Run);
    t.Start(); // 启动
    通过判断线程状态,决定是否唤醒线程。
    if (t.ThreadState == ThreadState.Suspended) // 如果被挂起了,就唤醒
    {
    t.Resume();
    }
    暂停就挂起线程:
    t.Suspend(); // 停止,挂起线程
     
    注:也可以定义一个开关,用来控制开始和结束,在开关为false的时候,直接continue,这样表现为暂停输出,但是实际上线程一直在运行。
     
    二、界面和代码
     

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Windows.Forms;
    using System.Threading;
    
    namespace WindowsFormsApplication3
    {
        public partial class Form2 : Form
        {
            public Form2()
            {
                InitializeComponent();
            }
    
            /// <summary>
            /// 因为控件的Invoke方法需要接收委托变量,因此需要定义委托和委托变量
            /// 定义一个委托,接收一个参数
            /// </summary>
            /// <param name="msg"></param>
            public delegate void DelegateFun(string msg);
            /// <summary>
            /// 定义一个委托变量
            /// 这个委托变量,需要初始化指定具体的方法;然后传递给控件的Invoke方法调用。
            /// </summary>
            public DelegateFun Fun1;
    
    
            /// <summary>
            /// 定义一个线程,处理数据,并更新界面
            /// </summary>
            private Thread t = null;
            // 开始按钮
            private void button1_Click(object sender, EventArgs e)
            {
                this.Invoke(Fun1, "开始...");          
              
                // 增加判断,避免每次单击都开辟一个线程
                if (t == null)
                {
                    t = new Thread(Run);
                    t.Start();   
                }
                if (t.ThreadState == ThreadState.Suspended) // 如果被挂起了,就唤醒
                {
                    t.Resume();
                }
                   
            }
            // 结束执行
            private void button2_Click(object sender, EventArgs e)
            {           
                t.Suspend(); // 停止,挂起线程
                this.Invoke(Fun1, "...停止");
            }
            
            // 具体做事情的方法
            public void Run()
            {
                //...... 处理一些事情,然后输出日志
                int i = 0;           
                while (true)
                {               
                    i++;
                    // this指Form2
                    //Invoke指: 在拥有控件的基础窗口句柄的线程上,用指定的参数列表执行指定委托。
                    //Invoke的参数是一个委托类型,因此必须定义委托变量 
                    this.Invoke(Fun1, i.ToString());               
                }
            }
    
            //在form初始化的时候,给委托变量赋值具体的方法
            private void Form2_Load(object sender, EventArgs e)
            {
                //给委托变量初始化具体的执行方法
                Fun1 = Print;
            }
    
            // 输出日志的方法
            public void Print(string msg)
            {
                // 新开辟的线程,不能直接调用这个方法。原因是控件只能由创建它的线程调用。
                // 其他线程调用提示错误: 线程间操作无效: 从不是创建控件“richTextBox1”的线程访问它。
                this.richTextBox1.AppendText(msg + "
    ");
                this.richTextBox1.ScrollToCaret();
            }
    
        }
    }
     

     
    三、参考文章
    1, C#多线程解决界面卡死问题的完美解决方案,BeginInvoke而不是委托delegate
  • 相关阅读:
    JVM 内部运行线程介绍
    JAVA多线程创建与退出过程
    各种 Java Thread State【转载】
    从Tomcat无法正常关闭讲讲Java线程关闭问题【转载】
    Class.forName和ClassLoader.loadClass的比较【转载】
    .NET Core、DNX、DNU、DNVM、MVC6学习资料
    Redis 资源
    Mongodb 资源
    部署Redis for Windows服务
    Mongodb副本集搭建经验
  • 原文地址:https://www.cnblogs.com/wang7/p/5869399.html
Copyright © 2020-2023  润新知