zoukankan      html  css  js  c++  java
  • 初探异步编程(1)

    理解概念

    进程:占用资源的最小单元(相当于工作车间)

    线程:调度运算的最小单元(相当于车间内的工人)

    1.在使用winform窗体程序时,碰到耗时操作经常会导致窗体假死的情况;

     那怎么解决UI线程的假死问题呢?

    可以使用异步关键字async和await将阻塞的线程释放出来,解决窗体在操作耗时函数的时候造成的假死问题。

    异步方法async可以修饰的返回值类型

    void

    Task:表示返回的是一个异步任务

    Task<T>:表示返回的是一个返回值为T类型的异步任务

    await 等待的是什么?

    对应上述三种分别是void Task 带返回值的Task

    那Task是什么?

    Task表示异步操作,可以理解为在另一个线程执行任务,而不阻塞当前线程;

    如何使用Task?

    调用Start()方法后会在另一个线程中立即执行DoJob()函数中的内容,同时也会在主线程中往下执行MessageBox.Show函数,直到程序向下执行碰到t.Wait或者t.Result方法阻塞主线程,在这里等待Task的异步任务处理完成再往下执行。

    //方法一
    Task<string> t = new Task<string>(() => { return DoJob(); });
    t.Start();
    MessageBox.Show("继续干活");
    t.Wait();//阻塞主线程,等待任务完成
    DoJob()就是一个返回值为string的普通函数;

    这里使用构造函数建立的异步任务,参数是一个Action委托,里面放需要异步执行的工作,注意需要执行t.Start();才能执行任务;

    //方法二
    Task<string> tf = Task.Factory.StartNew(() => { return DoJob(); });
    MessageBox.Show("继续干活,在线程:" + Task.CurrentId);
    tf.Wait();//阻塞主线程,等待任务完成

    这里使用工厂模式直接执行DoJob任务

    //方法三         
    Task<string> t = Task.Run(() => { return DoJob(); });
    MessageBox.Show("继续干活");
    string str = t.Result;//阻塞主线程,等待任务完成

    使用Task的Run方法也是直接执行DoJob任务

    由于上述的三种方法均使用了 .Result 或者 .Wait这两个会阻塞主线程的方法,都会使得UI界面假死,因此就需要使用async和await关键字将线程释放出来。 

    2.async和await关键字的使用情况:

    主线程执行代码:

    Task<string> tas = DoJobAsync();//已经开始执行任务
    this.button1.Text = "正在后台处理数据...";
    MessageBox.Show("继续干活");

     自己编写的DoJobAsync异步方法的代码

        //自己编写的异步方法
            private Task<string> DoJobAsync()
            {
                Task<string> t =  Task.Run(() => {
                    //需要异步执行的耗时工作量
                    for (int i = 0; i < 1000; i++)
                    {
                        Thread.Sleep(5);//模拟耗时操作
                        Console.WriteLine("AnotherJob IN Thread:" + Thread.CurrentThread.ManagedThreadId + " Job ExeTimes: " + i.ToString());
                    }
                    MessageBox.Show("异步任务Success");
                    return "任务完成";
                });
                return t;
            }    

    主线程在执行

    Task<string> tas = DoJobAsync();

     这段代码后会立即向下执行这段代码,而且DoJobAsync的工作是交给了另一个线程进行处理;

    this.button1.Text = "正在后台处理数据...";

    如果需要拿到DoJobAsync的返回值怎么办呢?这里的返回值是string

    使用

    string str = await tas;

    这里使用await 关键字修饰,方法就必须使用async关键字修饰,否则就不是异步方法。

    因此方法体内使用了await关键字,那么改方法就必须用async修饰,例如Button Click事件就必须要改写为

    private async void button1_Click(object sender, EventArgs e)
    {
        string str = await DoJobAsync();
    }

    这样的方式是不会阻止主线程,例如UI线程,不会陷入假死状态。

    await 时是释放线程,线程能去执行其它任务;.Result 和 .Wait 就是让线程暂停,等待结果。

    3.如何监控异步任务的执行进度?

     通过IProgress<T>, progress只提供了一个方法void Report(T value),通过Report方法把一个T类型的值报告给IProgress,然后IProgress<in T>的实现类Progress<in T>的构造函数接收类型为Action<T>的形参,通过这个委托让进度显示在UI界面中。

     实际就是干活的异步方法必须要有一个IProgress<T>参数,然后通过Report(T value)函数将进度反馈出去;

    实现类怎么构建呢?

    Progress<in T>通过构造函数生成,参数的类型是Action<T>的委托,这个委托可以把进度细节暴露到干活的异步方法之外,就实现了例如将进度写到UI线程上。

    代码:

            //主线程调用函数
            private async Task Display()
            {
                //当前线程
                Progress<int> progress = new Progress<int>(percent =>
                {
                    this.progressBar1.Value = percent;//在当前线程修改控件,因此不会抛出异常
                    Console.WriteLine("{0}%", percent);
                });
                //线程池线程
                //await Task.Run(() => DoProcessing(progress));
                await DoJobAsync(progress);
                Console.WriteLine("");
                Console.WriteLine("结束");
            }    
            //自己编写的异步方法
            private Task<string> DoJobAsync(IProgress<int> progress)
            {
                Task<string> t =  Task.Run(() => {
                    //需要异步执行的耗时工作量
                    for (int i = 0; i < 1000; i++)
                    {
                        if (progress != null)
                        {
                            progress.Report(i*100/1000);//返回进度执行情况
                        }
                        Thread.Sleep(5);//模拟耗时操作
                        Console.WriteLine("AnotherJob IN Thread:" + Thread.CurrentThread.ManagedThreadId + " Job ExeTimes: " + i.ToString());
                    }
                    MessageBox.Show("异步任务Success");
                    return "任务完成";
                });
                return t;
            }    

    主函数直接在Button Click事件中调用即可。

    //显示进度
    Task ts = Display();
    await ts;
        

    End

  • 相关阅读:
    在 Android 4.1上,分析 input -- android framework 部分 2
    Linux内核spin_lock、spin_lock_irq 和 spin_lock_irqsave 分析
    module_init 和 late_initcall 区别
    在 Android 4.4.4 上,分析 input -- android framework 部分
    Android 输入系统 与 按键
    INIT_WORK和INIT_DELAYED_WORK详解
    Android 中多点触摸协议
    android 电容屏(四):驱动调试之驱动程序分析篇 -- FocalTech
    android 电容屏(三):驱动调试之驱动程序分析篇
    android 电容屏(二):驱动调试之基本概念篇
  • 原文地址:https://www.cnblogs.com/LeeSki/p/12200062.html
Copyright © 2011-2022 走看看