zoukankan      html  css  js  c++  java
  • 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方法就不会抛出异常了,成功执行完毕。

  • 相关阅读:
    CocosCreator-Widget,前后台切换
    Unity获取未激活游戏对象的方法 、坐标转换
    Mathf函数
    C# activeSelf、activeInHierarchy、SetActive、SetActiveRecursively
    C# 碰撞,射线,点击,周期函数等基本代码
    TCP/IP 协议栈
    笔记—《程序员自我修养》
    Container With Most Water 双指针法
    多线程服务器 编程模型
    c++ 高效并发编程
  • 原文地址:https://www.cnblogs.com/OpenCoder/p/9824507.html
Copyright © 2011-2022 走看看