Java自学者论坛

 找回密码
 立即注册

手机号码,快捷登录

恭喜Java自学者论坛(https://www.javazxz.com)已经为数万Java学习者服务超过8年了!积累会员资料超过10000G+
成为本站VIP会员,下载本站10000G+会员资源,会员资料板块,购买链接:点击进入购买VIP会员

JAVA高级面试进阶训练营视频教程

Java架构师系统进阶VIP课程

分布式高可用全栈开发微服务教程Go语言视频零基础入门到精通Java架构师3期(课件+源码)
Java开发全终端实战租房项目视频教程SpringBoot2.X入门到高级使用教程大数据培训第六期全套视频教程深度学习(CNN RNN GAN)算法原理Java亿级流量电商系统视频教程
互联网架构师视频教程年薪50万Spark2.0从入门到精通年薪50万!人工智能学习路线教程年薪50万大数据入门到精通学习路线年薪50万机器学习入门到精通教程
仿小米商城类app和小程序视频教程深度学习数据分析基础到实战最新黑马javaEE2.1就业课程从 0到JVM实战高手教程MySQL入门到精通教程
查看: 751|回复: 0

C#中如果用await关键字来await一个为null的Task对象会抛出异常

[复制链接]
  • TA的每日心情
    奋斗
    2024-11-24 15:47
  • 签到天数: 804 天

    [LV.10]以坛为家III

    2053

    主题

    2111

    帖子

    72万

    积分

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    726782
    发表于 2021-7-15 13:47:08 | 显示全部楼层 |阅读模式

    await & async模式是C#中一个很重要的特性,可以用来提高异步程序(多线程程序)的执行效率。但是如果尝试用await关键字来await一个为null的Task对象,会导致程序抛出NullReferenceException异常。

     

    新建一个.NET Core控制台项目,贴入如下代码:

    using System;
    using System.Threading;
    using System.Threading.Tasks;
    
    namespace AwaitNull
    {
        class Program
        {
            /// <summary>
            /// AwaitNullTask方法中的代码会await一个为null的Task t,这样做会抛出NullReferenceException异常
            /// </summary>
            static async Task AwaitNullTask()
            {
                Task t = null;//声明一个为null的Task对象t
    
                try
                {
                    await t;//await为null的Task对象t,会导致这里抛出NullReferenceException异常
    
                    Console.WriteLine($"Thread id <{Thread.CurrentThread.ManagedThreadId}> : Await finished...");//由于上面抛出了异常,这里的Console.WriteLine不会被执行
                }
                catch(NullReferenceException e)
                {
                    //输出异常信息
                    Console.WriteLine($"Thread id <{Thread.CurrentThread.ManagedThreadId}> : AwaitNullTask threw Exception : {e.GetType().ToString()}");
                    Console.WriteLine($"Thread id <{Thread.CurrentThread.ManagedThreadId}> : Exception message : {e.Message}");
    
                    throw;//catch后继续抛出NullReferenceException异常到AwaitNullTask方法的外部
                }
            }
    
            static void Main(string[] args)
            {
                Task taskReturned = AwaitNullTask();//很有意思的是虽然AwaitNullTask方法内部抛出了NullReferenceException异常,但是其并不会影响AwaitNullTask方法外部的方法,就好像AwaitNullTask方法是在另外一个线程上执行的一样,但是本例中我们没有用Task来启动任何线程,可以看到本例中所有Console输出的信息中Thread id都相同,是在同一个线程上
    
                Console.WriteLine($"Thread id <{Thread.CurrentThread.ManagedThreadId}> : taskReturned status is : {taskReturned.Status.ToString()}");//输出AwaitNullTask方法返回的Task对象taskReturned的Status,由于AwaitNullTask方法内部抛出了异常,所以Task对象taskReturned的Status为Faulted
    
                Console.WriteLine();
                Console.WriteLine($"Thread id <{Thread.CurrentThread.ManagedThreadId}> : Press any key to quit...");
                Console.ReadKey();
            }
        }
    }

    输出结果如下:

    我们可以看到AwaitNullTask方法中由于await了一个为null的Task对象,抛出了NullReferenceException异常,但是有意思的是,AwaitNullTask方法表现出的行为是貌似运行在另一个线程上一样,其抛出的NullReferenceException异常并不会影响到Main方法,但实际上Main方法和AwaitNullTask方法都是由同一个线程运行的。

     

    所以我们在使用await关键字等待一个Task或Task<T>对象时,最好加上一个判断逻辑,只有在Task或Task<T>对象不为null时,才进行await。所以我们更改本例中上面的代码如下:

    using System;
    using System.Threading;
    using System.Threading.Tasks;
    
    namespace AwaitNull
    {
        class Program
        {
            /// <summary>
            /// 现在我们在AwaitNullTask方法中加了判断逻辑,只有在Task对象不为null时,才进行await,避免抛出异常
            /// </summary>
            static async Task AwaitNullTask()
            {
                Task t = null;//声明一个为null的Task对象t
    
                try
                {
                    //判断Task对象t是否为null,不为null才执行下面的await
                    if (t != null)
                    {
                        await t;//因为现在上面的if存在,这里的await就不会被执行了,避免了异常的抛出
                        Console.WriteLine($"Thread id <{Thread.CurrentThread.ManagedThreadId}> : Await finished...");
                    }
                }
                catch(NullReferenceException e)
                {
                    //输出异常信息
                    Console.WriteLine($"Thread id <{Thread.CurrentThread.ManagedThreadId}> : AwaitNullTask threw Exception : {e.GetType().ToString()}");
                    Console.WriteLine($"Thread id <{Thread.CurrentThread.ManagedThreadId}> : Exception message : {e.Message}");
    
                    throw;//catch后继续抛出NullReferenceException异常到AwaitNullTask方法的外部
                }
    
                //返回类型为Task的异步方法(使用了async关键字的方法),可以不返回任何值,或者使用return;也可以,.NET会在异步方法结束时自动构造一个Task对象,作为异步方法的返回值
            }
    
            static void Main(string[] args)
            {
                Task taskReturned = AwaitNullTask();//这次AwaitNullTask方法没有抛出异常成功返回
                
                Console.WriteLine($"Thread id <{Thread.CurrentThread.ManagedThreadId}> : taskReturned status is : {taskReturned.Status.ToString()}");//由于AwaitNullTask方法成功返回,所以此时Task对象taskReturned的Status为RanToCompletion,表示AwaitNullTask方法成功执行完毕
                //比较有意思的是虽然我们在AwaitNullTask方法中没有return任何东西,但是AwaitNullTask方法还是返回了一个不为null的Task对象taskReturned,并且我们还在上面输出了Task对象taskReturned的Status值,说明.NET为AwaitNullTask方法构造了一个Task对象作为返回值
    
                Console.WriteLine();
                Console.WriteLine($"Thread id <{Thread.CurrentThread.ManagedThreadId}> : Press any key to quit...");
                Console.ReadKey();
            }
        }
    }

    输出结果如下:

    所以这次AwaitNullTask方法就不会抛出异常了,成功执行完毕。

     

    哎...今天够累的,签到来了1...
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|小黑屋|Java自学者论坛 ( 声明:本站文章及资料整理自互联网,用于Java自学者交流学习使用,对资料版权不负任何法律责任,若有侵权请及时联系客服屏蔽删除 )

    GMT+8, 2024-12-22 17:00 , Processed in 0.061448 second(s), 30 queries .

    Powered by Discuz! X3.4

    Copyright © 2001-2021, Tencent Cloud.

    快速回复 返回顶部 返回列表