事件多线程

事件(Event)

概念

事件是对象在外界第三方因素影响下发生的事情,而对外提供的一种消息机制

事件的两个参与者

  1. 发送者(Sender):对象本身,当本身状态发生变化时,触发事件,并通知事件的接收者
  2. 接收者(Receiver):用来处理事件的,在事件发送者触发一个事件后,会自动执行的内容

事件和委托

相同点:

事件对象本质就是一个私有的委托对象,以及公有的方法,add(+=)和remove(-=)

不同点:

  1. +=方法是,实际上是调用add方法对委托进行添加。
  2. 委托对象私有以后,无法直接从外部赋值(内部可以赋值)。
  3. 好处:能够避免用户直接将对象事件清除,比如微软给内部事件,只允许通过add或者remove进行注册和移除事件,而不能清除掉。这样做能够保护委托。理解事件是对委托的封装

异步编程

同步方法

程序运行的时候,在调用其他方法的时候,会等待被调用的方法按顺序执行完,才会继续执行。非常符合开发思维,有序执行

异步方法

在程序调用异步方法的时候,主程序不会等待方法执行完,而是主程序调用异步方法后直接继续运行,而异步方法会启动一个新线程来完成方法的计算。

异步编程

委托的异步调用

  1. 先根据比较耗时的功能方法声明对应的委托

  2. 给委托赋值,赋与这个耗时的功能函数

  3. 采用委托的BeginInvoke方法进行异步调用委托

    Invoke方法:类似于默认的方法调用,相当于直接使用()调用委托
    BeginInvoke方法:指采取异步调用委托

  4. 给BeginInvoke方法创建回调函数

    回调函数:回调函数是指,当前的正在执行的操作完成之后,立刻回调用的一个函数是回调函数

    BeginInvoke方法参数组成:

    1. 第一组参数:委托所指向的方法对应的参数
    2. AsyncCallback参数:与当前委托对应的回调函数
    3. object参数:假如回调函数需要参数则由object参数提供

    BeginInvoke方法返回值为IAsyncResult,而委托的回调函数必须有一个参数为IAsyncResult类型

  5. 通过EndInvoke获取对应委托的BeginInvoke的执行结果

  6. 因为BeginInvoke相当于重新创建的线程进行异步调用方法函数,所以回调函数也是异步的线程的执行的,那么异步线程和程序主线程相当于阳关道和独木桥,

    因此如果使用Winform或WPF中的控件要考虑清楚:UI中的控件等都是Winform或WPF主线程中创建的,而异步线程无法直接使用

    设置CheckForIllegalCrossThreadCalls =false,这个属性是用来设置Winform或者WPF中的主线程不在做控件的跨线程检测

    public partial class Form1 : Form
    {
        public Form1()
        {
            CheckForIllegalCrossThreadCalls = false;
            InitializeComponent();
            func1 = Fun1;
        }
        Func<int,int, int> func1;
      
        private void button1_Click(object sender, EventArgs e)
        {
            IAsyncResult result = func1.BeginInvoke(2,10,new AsyncCallback(CallBackFunc),null);
            lbl2.Text =Fun2(10).ToString();
        }

        void CallBackFunc(IAsyncResult result)
        {
            this.lbl1.Text= func1.EndInvoke(result).ToString();
        }

        int Fun1(int num,int num2)
        {
            System.Threading.Thread.Sleep(5000);//等待5秒钟
            return num * num;
        }

        int Fun2(int num)
        {
            return num * num;
        }
    }

异步编程总结

  1. 异步编程是建立在委托基础上的一种编程方法
  2. 异步调用的每个方法都是独立的线程执行。因此。本质上就是一种多线程程序,也可以说是一种简化版本的多线程技术
  3. 比较适合在后台运行较为耗时的简单任务,并且任务要求相互独立,任务中不应该有代码直接访问可视化控件
  4. 如果后台任务要求必须按照特定顺序执行,或者必须访问公共资源,则异步编程不适合,而直接使用多线程技术

多线程的异步

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

        private void button1_Click(object sender, EventArgs e)
        {
            int a = 0;
            //多线程编程
            //委托:ParameterizedThreadStart
            Thread objThread1 = new Thread(() =>
            {
                for (int i = 0; i < 10; i++)
                {
                    a += i;
                    //控件的属性:作用是用来判断当前控件所在的使用环境是创建这个控件的线程(false)还是其他线程(true)
                    if (label1.InvokeRequired)
                    {
                        //控件的Invoke方法:方法中的第一个参数返回值为void的委托,第二个参数是给委托对应方法传递的参数
                        label1.Invoke(new Action<string>(s =>
                        {
                            label1.Text = $"【{s}】";
                        }), a.ToString());
                    }
                    Thread.Sleep(500);
                }
            });
            objThread1.Start();
        }
        private void button2_Click(object sender, EventArgs e)
        {
            int a = 0;
            Thread objThread02 = new Thread(() =>
            {
                for (int i = 0; i < 100; i++)
                {
                    a += i;
                    if (label2.InvokeRequired)
                    {
                        label2.Invoke(new Action<int>(s =>
                        {
                            label2.Text = $"【{s}】";
                        }), a);
                        System.Threading.Thread.Sleep(200);
                    }
                }
            });
            objThread02.Start();
        }
    }

如果创建的子线程未执行完成,当主线程关闭时,这时候会抛异常

在这里插入图片描述

这个错误的原因是:当主线程关闭时主线程中的labal2对象也跟着被释放了,而子线程中还依然在使用label2对象,子线程使用时发现label2没了,因此抛了这样一个异常

如果按照程序设计来分析:主线程都关闭了,对应的其下的子线程都要跟着关闭。

新的问题:主线程关闭,子线程如果未完成则没有关闭

解决方法:将程序中的子线程设置为后台线程则只要前台线程都关闭了,则后台线程自动关闭

  objThread1.IsBackground = true;

后台线程

指的是程序中的一些功能不需要和一般线程同时执行,或者这些功能对于UI界面中显示方面要求不高,可以设置为后台线程

例如:VS工具中的,编译检测功能就是后台线程,当VS启动起来,除过前台线程给我们展示编辑菜单等界面,同时后台运行了检测程序

多线程

概念

  1. 进程:每一个独立的程序都是进程。操作系统有《进程管理模块》,管理同时运行的多个程序
  2. 进程和线程:一个进程可以有若干个线程
http://www.ruanyifeng.com/blog/2013/04/processes_and_threads.html

句柄(Handle)

句柄是一个标识符,用于代表操作系统中的各种资源,比如各种窗体、GDI绘图对象、进程和线程对象、文件等都用句柄

句柄使用

  1. 即使进程退出,很多时候操作系统仍然保持进程句柄,操作系统句柄有限,使用用需要节省,比如打开文件后,使用完毕要及时关闭
  2. 句柄可以通过Process对象的Handle属性访问,比如进程退出的代码,时间等

总结:拥有图形界面的程序都有一个主窗体,这个窗体也对应有一个句柄。当窗体关闭时进程也关闭。主窗体一般由主线程负责创建

发布了148 篇原创文章 · 获赞 365 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/chonbi/article/details/105078831