.NET Core随笔之async、await

0. 摘要

  • 为探究async、await对程序执行的影响,本文从被async修饰的方法的方法体中是否调用新线程以及是否使用await调用异步方法这两个条件来进行分析,在四种情况下得出await提高线程利用率的结论

1. 实验

1.1 实验准备

代码准备:准备两个耗时方法Func1和Func2,两个方法都由async修饰且功能相似,都是进行一个没有意义耗时的操作。但区别是Func2中方法的实现是通过Task.Run()来执行的。

代码说明:Thread.CurrentThread.ManagedThreadId可获得当前正在使用的线程的Id,运行结果中不同的数字代表不同的线程

  • async Task<decimal> Func1(int n)
    {
        Console.WriteLine("Func1_start: " + Thread.CurrentThread.ManagedThreadId);
        decimal result = 1;
        Random random = new Random();
        for (int i = 0; i < n * n; i++)
        {
            result += (decimal)random.NextDouble();
         
         Console.WriteLine("Func1_end: " + Thread.CurrentThread.ManagedThreadId);
         return result;
    }
    
    async Task<decimal> Func2(int n)
    {
        Console.WriteLine("Func2_start: " + Thread.CurrentThread.ManagedThreadId);
        return await Task.Run(() =>
        {
             decimal result = 1;
             Random random = new Random();
             for (int i = 0; i < n * n; i++)
             {
                 result += (decimal)random.NextDouble();
             }
             Console.WriteLine("Func2_end: " + Thread.CurrentThread.ManagedThreadId);
             return result;
        });
    }

1.2 实验开始

(1)对于方法体内没有调用新线程的异步方法Func1(仅由async声明的异步方法)

  • a. 不用await调用
    • Console.WriteLine("Main:" + Thread.CurrentThread.ManagedThreadId);
      Func1(10000);
      Console.WriteLine("Main:" + Thread.CurrentThread.ManagedThreadId);
    • 运行结果

      Main:1
      Func1_start: 1
      Func1_end: 1
      Main:1

  • b. 使用await调用
    • Console.WriteLine("Main:" + Thread.CurrentThread.ManagedThreadId);
      await Func1(10000);
      Console.WriteLine("Main:" + Thread.CurrentThread.ManagedThreadId);
    • 运行结果

      Main:1
      Func1_start: 1
      Func1_end: 1
      Main:1

  • 分析:当方法体内没有调用新线程的方法时,即便被asycn修饰,仍会同步执行,不会实现异步的效果;且无论是否使用await调用都是同步执行,不会有变化。

(2)对于方法体内有调用新线程的异步方法Func2(如Task.Run() 或 Task.Factory.StartNew() 等)

  •  a. 不用await调用
    • Console.WriteLine("Main:" + Thread.CurrentThread.ManagedThreadId);
      Func2(10000);
      Console.WriteLine("Main:" + Thread.CurrentThread.ManagedThreadId);
    • 运行结果1

      Main:1
      Func2_start: 1
      Main:1

    • Console.WriteLine("Main:" + Thread.CurrentThread.ManagedThreadId);
      Func2(10);
      Console.WriteLine("Main:" + Thread.CurrentThread.ManagedThreadId);
    • 运行结果2(为了让Fun2快点结束,把参数设置的很小)

      Main:1
      Func2_start: 1
      Main:1
      Func2_end: 4

    • 分析:
      1. 从结果1可以看出方法Func2还没执行完,主线程Main-1就继续执行了。因此,可得出方法体内有Task.Run()的异步方法可以实现异步效果
      2. 把Func2的参数值设小一点让Fun2快点结束,从结果2可以看到执行此异步方法的线程是另一个线程Func2_end:4
      3. 也可看出不使用await的情况下主线程并没有切换
        1. 说明在执行异步方法的时候,主线程是一直被阻塞,直到异步方法执行结束,才继续执行后面的代码
        2. 此外,之后的代码如果需要异步方法Func2的的执行结果,还可能会报错(因为Func2还没执行结束,主程序那不到Func2的结果)
  •  b. 用await调用
    • Console.WriteLine("\n");
      await Func2(10000);
      Console.WriteLine("Main:" + Thread.CurrentThread.ManagedThreadId);
    • 运行结果:下面是执行了多次的结果,基本就是下面两种结果

      //结果1

      Main:1
      Func2_start: 1
      Func2_end: 5
      Main:5

      //结果2

      Main:1
      Func2_start: 1
      Func2_end: 4
      Main:4

    • 分析:
      1. 从结果可以看出主线程已经发生了变化,说明是异步执行的。
      2. 由于主线程切换,说明最开始使用的线程Main-1已经被放回线程池,等别的任务需要用到时线程Main-1时就可以去执行了,而不会因异步方法的执行而阻塞提高了线程的利用率。
      3. 与不使用await调用方法对比,使用await调用异步方法会等此异步方法结束后才执行之后的代码。
      4. 执行异步方法的线程是新线程,这个新线程执行完异步方法后会接着执行之后主程序中的代码。

2. 结论

  1. 在async声明的方法中,如果要实现异步,还需要方法体中调Task.Run (或者其他可以调用新线程的方法,如Task.Factory.StartNew()等)。
  2. await 仅对用async声明内部调用了新线程的异步方法起到作用,并会以同步的方式调用异步的方法;因此,想要真正的利用起来awiat,需要对内部有Task.Run的异步方法进行调用。
  3. 用await调用由async声明内部调用了新线程的异步方法,执行流程是:
  • 线程A执行主程序代码→执行到await 异步方法()中的Task.Run时,线程A回到线程池,由线程B来执行此异步方法→异步方法执行功能结束后,线程B不释放,而是直接执行主程序后面的代码

     4.合理的使用async、await可以提高线程的利用率,避免线程阻塞,提高系统的并发量。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值