必须了解的C#异步编程

异步编程

一、同步和异步概念

同步方法

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

异步方法

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

二、委托异步

(一)委托的异步调用

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中的控件等都是WinformWPF主线程中创建,而异步线程无法直接使用
设置CheckForIllegalCrossThreadCalls =false,这个属性是用来****设置Winform或者WPF中的主线程不在做控件的跨线程检测

(二)委托异步案例

1.求一个数的乘积

		public Form1()
        {
            //5.必须关闭主线程检查,不然会报错(在异步线程中无法使用,所以要关闭主线程检查)
            CheckForIllegalCrossThreadCalls = false;
            InitializeComponent();
            //2.给委托赋值,赋给耗时功能函数
            func1 = Fun1;

        }
        //1.根据功能方法声明对应的委托
        Func<int, int> func1;  //系统自定义的委托 第一个为实参,第二个为返回参数
        private void button1_Click(object sender, EventArgs e)
        {
            // lab1.Text = Fun1(5).ToString() ;
            //3.通过BeginInvoke方法进行异步调用,此方法需调用委托 有两个可选参数,第一个是AsyncCallback委托是异步完成的回调方法。第二个是用户自定义对象,该对象将传递到回调方法中
            IAsyncResult result = func1.BeginInvoke(5,new AsyncCallback(CallBackFunc),null);
            lab2.Text = Fun2(6).ToString();
        }

        //4.回调函数,调用了BeginInvoke,就必须调用EndInvoke 结束异步
        void CallBackFunc(IAsyncResult result)
        {
            //5.通过EndInvoke检查异步调用
            this.lab1.Text = func1.EndInvoke(result).ToString(); //  如果异步调用尚未完成,EndInvoke将阻塞调用线程,直到它完成
        }
        //功能一
        int Fun1(int num)
        {
            System.Threading.Thread.Sleep(500);//设置等待时间
            return num * num;
        }
        //功能二
        int Fun2(int num)
        {
            return num * num;
        }

2.求数的阶乘,求相同数的乘积

 public Form1()
        {
            InitializeComponent();
            CheckForIllegalCrossThreadCalls = false;//关闭主线程
            //1-2.2.实例化委托(委托赋值)
            mydelegate = ExecJie;
            //2-2.2实例化委托
            calcator = new MyCalcator(ExecuteTask);
        }
        //1-1.创建委托
        public delegate int MyDeleas(int num);
        //1-2.1声明委托实例
        MyDeleas mydelegate;
        private void button1_Click(object sender, EventArgs e)
        {
            //1-3.调用委托的异步方法BeginInvoke
            IAsyncResult rest = mydelegate.BeginInvoke(6, new AsyncCallback(CallBackFunc), null);

        }
        //1-4.创建回调函数 EndInvoke检查异步
        void CallBackFunc(IAsyncResult async)
        {
            label1.Text = mydelegate.EndInvoke(async).ToString();
        }
        //功能1 求阶乘
        int ExecJie(int num)
        {
            int res = 1;
            for (int i = 1; i < num; i++)
            {
                System.Threading.Thread.Sleep(300); //等待时间
                res *= i;
            }
            return res;
        }

        //2-1.创建委托 与功能参数相同
        public delegate int MyCalcator(int num,int it);
        //2.1声明委托
        MyCalcator calcator = null; //因为有多个参数需此声明
        private void button2_Click(object sender, EventArgs e)
        {
            //2-3.调用委托异步方法BeginInvoke
            for (int i = 0; i < 11; i++)
            {
                calcator.BeginInvoke(i * 6, i * 1000,CallBackFunction,null); //前两个为自定义参数,第一个参数为求乘积的数,第二为间隔时间参数,第三个为回调,第四个为调用对象
            }
        }
        //2-4.创建回调函数
        void CallBackFunction(IAsyncResult async)
        {
            int res = calcator.EndInvoke(async);
            MessageBox.Show(res.ToString());
        }
        //功能2 求相同数成积
        int ExecuteTask(int num,int it)
        {
            System.Threading.Thread.Sleep(it); //等待时间
            return num * num;
        }

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

三、多线程异步

多线程异步案例

1.求乘积

		//求乘积
        private void button1_Click(object sender, EventArgs e)
        {
            int a = 0;
            //委托:ParameterizedThreeadStart 
            Thread objThread1 = new Thread(() =>
              {
                  for (int i = 0; i < 10; i++)
                  {
                      a += i;
                      //判断当前控件所在的使用环境是创建这个控件的线程还是其他线程(true)
                      if (label1.InvokeRequired)
                      {
                          label1.Invoke(new Action<string>(s =>
                          {
                              label1.Text = $"{s}";
                          }), a.ToString());
                      }
                      Thread.Sleep(500); //间隔时间
                  }
              });
            objThread1.IsBackground = true;//允许后台线程
            objThread1.Start();//开启线程
        }

2.求1-100之和

		//求1-100之和
        private void button2_Click(object sender, EventArgs e)
        {
            int a = 0;
            Thread objThread2 = 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);
                      }
                  }
              });
            objThread2.IsBackground = true;
            objThread2.Start();
        }

3.出现的异常

在这里插入图片描述

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

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

后台线程

扫描二维码关注公众号,回复: 10558168 查看本文章

指的是程序中的一些功能不需要和一般线程同时执行,或者这些功能对于UI界面中显示方面要求不高,可以设置为后台线程
例如:VS工具中的,编译检测功能就是后台线程,当VS启动起来,除过前台线程给我们展示编辑菜单等界面,同时后台运行了检测程序

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

猜你喜欢

转载自blog.csdn.net/dust__/article/details/105084573