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. 精选10款HTML5手机模板

    1.Stroller | Mobile & Tablet Responsive Template 演示地址   购买地址 2.Ocean Mobile Template 演示地址   购买地址 ...

  2. JavaWeb学习总结(十一):Session解决form表单重复提交

    在平时开发中,如果网速比较慢的情况下,用户提交表单后,发现服务器半天都没有响应,那么用户可能会以为是自己没有提交表单,就会再点击提交按钮重复提交表单,我们在开发中必须防止表单重复提交. 一.表单重复提 ...

  3. Java设计模式—备忘录模式

    个人感觉备忘录模式是一个比较难的设计模式,备忘录模式就是一个对象的备份模式,提供了一种程序数据的备份方法. 定义如下:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态.这样以 ...

  4. 利用Swig转换C++代码为C#可用的代码

    详细的文件路径为:http://user.qzone.qq.com/1259374136/blog/1432887689 Swig学习教程 1.Swig的基本介绍 SWIG(Simplified Wr ...

  5. slice()方法 和splice 方法的区别

    定义 splice() 方法 用于插入.删除或替换数组的元素. slice() 方法 可提取字符串的某个部分,并以新的字符串返回被提取的部分. 更多的可查看: http://www.cnblogs.c ...

  6. 交叉编译 Cross-compiling for Linux

    @(134 - Linux) Part 1 交叉编译简介 1.1 What is cross-compiling? 对于没有做过嵌入式编程的人,可能不太理解交叉编译的概念,那么什么是交叉编译?它有什么 ...

  7. centors7 elasticsearch6.3安装以及问题记录

    1.安装elasticsearch . 安装系统:centors7 1.下载安装包 官网地址:https://www.elastic.co/downloads/past-releases 2.mac文 ...

  8. MyBatis基本配置和实践(五)

    第一步:创建一个Maven工程 第二步:编辑Maven工程的pom.xml,引入mybatis-generator-maven-plugin <?xml version="1.0&qu ...

  9. d3js shape深入理解

    本文将视图了解d3js提供的帮助我们创建矢量图形的helper函数,比如下面的: http://d3indepth.com/shapes/ lines curves pie chart segment ...

  10. SQL创建视图

    1.视图的理解 从用户角度来看,一个视图是从一个特定的角度来查看数据库中的数据 .从数据库系统内部来看,一个视图是由SELECT语句组成的查询定义的虚拟表 .从数据库系统内部来看,视图是由一张或多张表 ...