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

  • 相关阅读:
    HDU 1075 What Are You Talking About(字典树)
    HDU 1075 What Are You Talking About (stl之map映射)
    HDU 1247 Hat’s Words(字典树活用)
    字典树HihoCoder
    HDU 1277全文检索(字典树)
    HDU 3294 Girls' research(manachar模板题)
    HDU 3294 Girls' research(manachar模板题)
    HDU 4763 Theme Section(KMP灵活应用)
    Ordering Tasks UVA
    Abbott's Revenge UVA
  • 原文地址:https://www.cnblogs.com/OpenCoder/p/9824507.html
Copyright © 2011-2022 走看看