万金流
以码会友。 吾Q:578751655。 水平有限,轻喷,谢!

世界很单纯,复杂的是人呐~

async和await也是如此。语法和含义很简单,程序员想多了,这东西就显得特别复杂。

async

含义:异步。它修饰的方法里,通常有其他异步操作。普通操作(通常是前半截)执行完了,控制权就返回“调用它的方法(主方法)”了(通常带回一个Task)。当异步操作(通常是后半截)执行完之后,主方法可以从它返回的Task里拿需要的东西(也可以不拿)。前后半截的分界线,就是await。

  ps:这东西一般出现在方法头部,但不属于方法签名的一部分。也就是说,不能出现在接口和抽象类的方法说明中,可以出现在实现的方法头。

await

  含义:在可等待方法中,await就是前后两段代码的分界线。它前面的代码被编译成一个方法,它和它后面的代码被编译成另一个方法。第一个方法,通常由主线程执行;第二个方法,通常由新建的线程(一般在异步嵌套调用的最里层新建)执行。它等待的,是一个异步操作的结果。

  注意:子方法运行到await的时候,主方法继续执行,而子方法等待它等待的结果,然后执行它后面的语句。

用途

  通常,设计一个异步(可等待)方法,就是为了方便别人调用它,它执行异步操作的时候,别人还能干自己的事。


问题来了:怎么用?

不止是语法问题,还要跟我们平时的编程习惯……最好是同步编程习惯贴合,才舒适。

先看一段耗时操作:

先花1秒,计算n!

static int jc(int x)
{
    int y = 1;
    Thread.Sleep(1000);
    for (int i = 2; i <=x; i++)
    {
        y *= i;
    }
    return y;
}

针对耗时操作,肯定会想到异步调用。常规异步调用:

var t=Task.Run(() => jc(5));//求5!,开始算吧
Console.WriteLine("我忙我的");
Console.WriteLine($"5!={t.Result}");

既然耗时操作,一般都是异步调用。那我就给它包装个异步方法,以期更好调用:

static async Task<int> jc1(int x)
{
    var t = Task.Run(() => jc(x));
    Console.WriteLine($"异步计算{x}!已经开始了,主程序继续...");
    await t;
    Console.WriteLine($"{x}!算完了");
    return t.Result;
    //return await Task.Run(() => jc(x));//极简写法
}

遇到await,主程序得到一个“必有int返回值”的承诺,就继续执行。子程序当Task执行完毕的时候,await向下继续执行。

调用方法就可以写成:

var t = jc1(5);//异步求5!,开始算吧,我继续。。。
Console.WriteLine("我忙我的");
Console.WriteLine($"5!={t.Result}");

第一行开始计算,第三行索要结果(谁让你承诺必有结果呢)。少了task,更符合人类习惯了。

第三行“t.Result”那里如果是“await t”,这个主方法也就是异步方法了。 


总结:

一、无论是自己写的,还是微软写的异步方法(下面叫做S)里,必然有一个多线程的所谓“耗时操作”。

二、调用时,可以

  1. await S();阻塞式异步调用;
  2. S().wait();阻塞式同步调用;
  3. var t=S();/*并行操作*/;await t;并行异步调用;
  4. var t=S();/*并行操作*/;t.wait();并行同步调用。

所谓异步/同步调用,即所在的方法是异步/同步方法。

三、不管套多少层的异步,一般也就两个线程。

 

posted on 2021-11-15 01:47  万金流  阅读(1597)  评论(0编辑  收藏  举报