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

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方法就不会抛出异常了,成功执行完毕。

 

转载于:https://www.cnblogs.com/OpenCoder/p/9824507.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值