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

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

  1. Async 与 Await 关键字研究

    1        Aynsc 和 Await 关键字的研究 在 .NET 4.0 以后,基于 Task 的异步编程模式大行其道,因其大大简化了异步编程所带来的大量代码工作而深受编程人员的欢迎,如果你曾 ...

  2. C#中的异步编程Async 和 Await

    谈到C#中的异步编程,离不开Async和Await关键字 谈到异步编程,首先我们就要明白到底什么是异步编程. 平时我们的编程一般都是同步编程,所谓同步编程的意思,和我们平时说的同时做几件事情完全不同. ...

  3. .NET中的async和await关键字使用及Task异步调用实例

    其实早在.NET 4.5的时候M$就在.NET中引入了async和await关键字(VB为Async和Await)来简化异步调用的编程模式.我也早就体验过了,现在写一篇日志来记录一下顺便凑日志数量(以 ...

  4. 浅谈async、await关键字 => 深谈async、await关键字

    前言 之前写过有关异步的文章,对这方面一直比较弱,感觉还是不太理解,于是会花点时间去好好学习这一块,我们由浅入深,文中若有叙述不稳妥之处,还请批评指正. 话题 (1)是不是将方法用async关键字标识 ...

  5. 多线线程async与await关键字

    创建线程 //这里面需要注意的是,创建Thread的实例之后,需要手动调用它的Start方法将其启动. //但是对于Task来说,StartNew和Run的同时,既会创建新的线程,并且会立即启动它. ...

  6. WebApi上传图片 await关键字

    await关键字对于方法执行的影响 将上一篇WebApi上传图片中代码修改(使用了await关键字)如下: [HttpPost] public async Task<string> Pos ...

  7. async和await关键字实现异步编程

    async和await关键字实现异步编程 异步编程   概念 异步编程核心为异步操作,该操作一旦启动将在一段时间内完成.所谓异步,关键是实现了两点:(1)正在执行的此操作,不会阻塞原来的线程(2)一旦 ...

  8. 为什么我们要使用Async、Await关键字

    前不久,在工作中由于默认(xihuan)使用Async.Await关键字受到了很多质问,所以由此引发这篇博文“为什么我们要用Async/Await关键字”,请听下面分解: Async/Await关键字 ...

  9. 异步编程Async/await关键字

    异步编程Async \await 关键字在各编程语言中的发展(出现)纪实. 时间 语言版本 2012.08.15 C#5.0(VS2012) 2015.09.13 Python 3.5 2016.03 ...

随机推荐

  1. 理解webpack4.splitChunks之maxInitialRequests

    maxInitialRequests是splitChunks里面比较难以理解的点之一,它表示允许入口并行加载的最大请求数,之所以有这个配置也是为了对拆分数量进行限制,不至于拆分出太多模块导致请求数量过 ...

  2. Struts2详讲

    一 概述 1.什么是Struts2? Struts2是一个在WebWork框架基础上发展起来开源MVC框架. 2.StrutsPrepareAndExecuteFilter StrutsPrepare ...

  3. a标签无法正确下载数据

    场景:做下载二维码功能,使用了a标签+download属性,在使用一段时候后,突然有一个数据下载时候无法识别文件格式,以前都是png下载 原因:因为download="文件名"这里 ...

  4. redux-devtools的使用

    1.浏览器里安装redux-devtools 2.在项目的入口文件里  找到 createStore函数调用的地方,给它加第二个参数 window.__REDUX_DEVTOOLS_EXTENSION ...

  5. HTML中的Head标签学习

    在页面加载完成的时候,标签head里的内容,是不会在页面中显示出来的.它包含了像页面的<title>(标题) ,CSS(如果你想用CSS来美化页面内容),图标和其他的元数据(比如 作者,关 ...

  6. .NET开源论坛MvcForum推荐

    MvcForum算是Asp.net中开源论坛佼佼者之一.主要使用ASP.NET MVC 5 &Unity & Entity Framework 6,有较强的可撸性.是论坛开发者的不二之 ...

  7. 【vue入门】日志demo,增删改查的练习(无vuex版本)

    安装 1. 确定电脑已装node和npm 出现版本号则说明电脑已经安装好node和npm2. 创建一个基于webpack的项目   3. 在项目里安装依赖 4. 运行 配置路由为了动态渲染各个页面的组 ...

  8. BubblePopupWindow

    Android 实现各个方向的气泡弹窗,可控制气泡尖角偏移量. https://github.com/smuyyh/BubblePopupWindow 截图 使用: BubblePopupWindow ...

  9. Java常见错误列表

    Java常见错误列表: 找不到符号(symbol) 类X是public的,应该被声明在名为X.java的文件中 缺失类.接口或枚举类型 缺失X 缺失标识符 非法的表达式开头 类型不兼容 非法的方法声明 ...

  10. Tsql 获取服务器信息

    Tsql 获取服务器属性,如服务器版本.服务器名 ref:http://technet.microsoft.com/zh-cn/library/ms174396.aspx select serverp ...