C#基础系列——异步编程初探:async和await
前言:前面有篇从应用层面上面介绍了下多线程的几种用法,有博友就说到了async, await等新语法。确实,没有异步的多线程是单调的、乏味的,async和await是出现在C#5.0之后,它的出现给了异步并行变成带来了很大的方便。异步编程涉及到的东西还是比较多,本篇还是先介绍下async和await的原理及简单实现。
C#基础系列目录:
- C#基础系列——Linq to Xml读写xml
- C#基础系列——扩展方法的使用
- C#基础系列——序列化效率比拼
- C#基础系列——反射笔记
- C#基础系列——Attribute特性使用
- C#基础系列——小话泛型
- C#基础系列——多线程的常见用法详解
- C#基础系列——委托和设计模式(一)
- C#基础系列——委托和设计模式(二)
- C#基础系列——再也不用担心面试官问我“事件”了
之前的那篇 C#基础系列——多线程的常见用法详解 就讲到了多线程new Thread()的方式对于有返回值类型的委托是没有解决方案的,如果需要返回值,必须要依靠异步的方式。了解异步之前,我们先来看看Thread对象的升级版本Task对象:
1、Task对象的前世今生:Task对象是.Net Framework 4.0之后出现的异步编程的一个重要对象。在一定程度上来说,Task对象可以理解Thread对象的一个升级产品。既然是升级产品,那它肯定有他的优势,比如我们上面Thread对象不能解决的问题:对于有返回值类型的委托。Task对象就能简单的解决。
static void Main(string[] args)
{
Console.WriteLine("执行GetReturnResult方法前的时间:" + DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss"));
var strRes = Task.Run<string>(() => { return GetReturnResult(); });//启动Task执行方法
Console.WriteLine("执行GetReturnResult方法后的时间:" + DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss"));
Console.WriteLine(strRes.Result);//得到方法的返回值
Console.WriteLine("得到结果后的时间:" + DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss")); Console.ReadLine();
} static string GetReturnResult()
{
Thread.Sleep();
return "我是返回值";
}
先来看结果:

从结果分析可知在执行var strRes = Task.Run<string>(() => { return GetReturnResult(); })这一句后,主线程并没有阻塞去执行GetReturnResult()方法,而是开启了另一个线程去执行GetReturnResult()方法。直到执行strRes.Result这一句的时候主线程才会等待GetReturnResult()方法执行完毕。为什么说是开启了另一个线程,我们通过线程ID可以看得更清楚:
static void Main(string[] args)
{
Console.WriteLine("执行GetReturnResult方法前的时间:" + DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss"));
var strRes = Task.Run<string>(() => { return GetReturnResult(); });
Console.WriteLine("执行GetReturnResult方法后的时间:" + DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss"));
Console.WriteLine("我是主线程,线程ID:" + Thread.CurrentThread.ManagedThreadId);
Console.WriteLine(strRes.Result);
Console.WriteLine("得到结果后的时间:" + DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss")); Console.ReadLine();
} static string GetReturnResult()
{
Console.WriteLine("我是GetReturnResult里面的线程,线程ID:" + Thread.CurrentThread.ManagedThreadId);
Thread.Sleep();
return "我是返回值";
}
结果:

由此可以得知,Task.Run<string>(()=>{}).Reslut是阻塞主线程的,因为主线程要得到返回值,必须要等方法执行完成。
Task对象的用法如下:
//用法一
Task task1 = new Task(new Action(MyAction));
//用法二
Task task2 = new Task(delegate
{
MyAction();
});
//用法三
Task task3 = new Task(() => MyAction());
Task task4 = new Task(() =>
{
MyAction();
}); task1.Start();
task2.Start();
task3.Start();
task4.Start();
由上可知,Task对象的构造函数传入的是一个委托,既然能传入Action类型的委托,可想而知Action的16中类型的参数又可以派上用场了。于是乎Task对象参数的传递就不用多说了吧。详见 C#基础系列——委托和设计模式(一)里面Action委托的用法。
2、初识 async & await。
static void Main(string[] args)
{
Console.WriteLine("我是主线程,线程ID:{0}", Thread.CurrentThread.ManagedThreadId);
TestAsync();
Console.ReadLine();
} static async Task TestAsync()
{
Console.WriteLine("调用GetReturnResult()之前,线程ID:{0}。当前时间:{1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss"));
var name = GetReturnResult();
Console.WriteLine("调用GetReturnResult()之后,线程ID:{0}。当前时间:{1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss"));
Console.WriteLine("得到GetReturnResult()方法的结果:{0}。当前时间:{1}", await name, DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss"));
} static async Task<string> GetReturnResult()
{
Console.WriteLine("执行Task.Run之前, 线程ID:{0}", Thread.CurrentThread.ManagedThreadId);
return await Task.Run(() =>
{
Thread.Sleep();
Console.WriteLine("GetReturnResult()方法里面线程ID: {0}", Thread.CurrentThread.ManagedThreadId);
return "我是返回值";
});
}
结果:

我们来看看程序的执行过程:

由上面的结果可以得到如下结论:
(1)在async标识的方法体里面,如果没有await关键字的出现,那么这种方法和调用普通的方法没什么区别。
(2)在async标识的方法体里面,在await关键字出现之前,还是主线程顺序调用的,直到await关键字的出现才会出现线程阻塞。
(3)await关键字可以理解为等待方法执行完毕,除了可以标记有async关键字的方法外,还能标记Task对象,表示等待该线程执行完毕。所以await关键字并不是针对于async的方法,而是针对async方法所返回给我们的Task。
(4)是否async关键字只能标识返回Task对象的方法呢。我们来试试:

异步方法的返回类型必须为void、Task或者Task<T>类型。也就是说async要么是void,要么和Task关联。
3、除了await关键字,Task对象还有另外一种方式等待执行结果。
static async Task TestAsync()
{
Console.WriteLine("调用GetReturnResult()之前,线程ID:{0}。当前时间:{1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss"));
var name = GetReturnResult();
Console.WriteLine("调用GetReturnResult()之后,线程ID:{0}。当前时间:{1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss"));
Console.WriteLine("得到GetReturnResult()方法的结果:{0}。当前时间:{1}", name.GetAwaiter().GetResult(), DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss"));
}
这样可以得到相同的结果。
name.GetAwaiter()这个方法得到的是一个TaskAwaiter对象,这个对象表示等待完成的异步任务的对象,并提供结果的参数。所以除了能完成await关键字的等待之外,它还能做一些其他的操作。我们将TaskAwaiter转到定义
public struct TaskAwaiter<TResult> : ICriticalNotifyCompletion, INotifyCompletion
{
public bool IsCompleted { get; }
public TResult GetResult();
public void OnCompleted(Action continuation);
public void UnsafeOnCompleted(Action continuation);
}
IsCompleted:获取一个值,该值指示异步任务是否已完成。
GetResult():得到执行的结果。这个方法和await关键字效果相同。
OnCompleted():传入一个委托,在任务执行完成之后执行。
UnsafeOnCompleted():计划与此 awaiter 相关异步任务的延续操作。
由此可以看出,await关键字实际上就是调用了TaskAwaiter对象的GetResult()方法。
C#基础系列——异步编程初探:async和await的更多相关文章
- 异步编程(Async和Await)的使用
.net4.5新特性之异步编程(Async和Await)的使用 一.简介 首先来看看.net的发展中的各个阶段的特性:NET 与C# 的每个版本发布都是有一个“主题”.即:C#1.0托管代码→C#2. ...
- .Net 4.5 异步编程初试(async和await)
.Net 4.5 异步编程初试(async和await) 前言 最近自己在研究Asp.Net Web API.在看到通过客户端来调用Web API的时候,看到了其中的异步编程,由于自己之前没有接触过, ...
- C# 异步编程,async与await的简单学习
前提声明:C# 5.0 .NET Framework 4.5 2012-08-15 异步和等待(async和await).调用方信息(Caller Information) (C#版本与.NET版本 ...
- C# 异步编程4 async与await 异步程序开发
随着C#异步程序开发系列的深入,你会发现编写异步程序越发简单.事物的发展就是这样的规律,从简单到复杂再到简单. 在C# 5.0中我们可以通过async与await关键字实现快捷的异步程序开发,如下: ...
- .NET4.5新特性之异步编程(Async和Await)的使用
一.简介 首先来看看.net的发展中的各个阶段的特性:NET 与C# 的每个版本发布都是有一个"主题".即:C#1.0托管代码→C#2.0泛型→C#3.0LINQ→C#4.0动态语 ...
- 多线程之异步编程: 经典和最新的异步编程模型,async与await
经典的异步编程模型(IAsyncResult) 最新的异步编程模型(async 和 await) 将 IAsyncInfo 转换成 Task 将 Task 转换成 IAsyncInfo 示例1.使用经 ...
- .NET异步编程初识async与await
这是两个关键字,用于异步编程.我们传统的异步编程方式一般是Thread.ThreadPool.BeginXXX.EndXXX等等.把调用.回调分开来,代码的逻辑是有跳跃的,于是会导致思路不是很清晰的问 ...
- 《C#并发编程经典实例》学习笔记—异步编程关键字 Async和Await
C# 5.0 推出async和await,最早是.NET Framework 4.5引入,可以在Visual Studio 2012使用.在此之前的异步编程实现难度较高,async使异步编程的实现变得 ...
- C# 异步编程(async&await)
同步:同步就是指一个进程在执行某个请求的时候,若该请求需要一段时间才能返回信息,那么这个进程将会一直等待下去,直到收到返回信息才继续执行下去 异步:异步是指进程不需要一直等下去,而是继续执行下面的操作 ...
随机推荐
- CSS项目学习总结
1.我过去在HTML和CSS阶段是如何学习的? 我一开始学HTML和CSS,更多的是通过看视频.书籍,一个知识点一个知识点地去学习,很少把他们串联起来,看代码多于敲代码. 然而,通过现在这几个项目的实 ...
- 走进 .Net 单元测试
走进 .Net 单元测试 Intro "不会写单元测试的程序员不是合格的程序员,不写单元测试的程序员不是优秀程序员." -- 一只想要成为一个优秀程序员的渣逼程序猿. 那么问题来了 ...
- iOS 多线程GCD简介
一.简介 1.1 GCD (Grand Central Dispatch )是Apple开发的一个多核编程的解决方法. Grand 含义是“伟大的.宏大的”,Central含义“中央的”,Dispat ...
- Objective-C 快速入门--基础(三)
1.OC有几种方式创建字符串对象?如:如何创建一个字符串对象:@“Baby”. OC中有3种方式创建字符串对象: 示例:main.m文件中: 控制台输出: 2.OC中如何获取字符串的长度? OC中获取 ...
- 深入理解JavaScript的闭包特性如何给循环中的对象添加事件
初学者经常碰到的,即获取HTML元素集合,循环给元素添加事件.在事件响应函数中(event handler)获取对应的索引.但每次获取的都是最后一次循环的索引.原因是初学者并未理解JavaScript ...
- 开启我的Android之旅-----记录Android环境搭建遇到的问题
在现在这个离不开手机的时代,对于手机APP的开发也是一个很大的市场,所以自己也想去探一探手机APP开发,在我们进行Android开发的第一步就是搭建环境,具体怎么搭建我就不说,这里记录一下在搭建环境的 ...
- 深圳 Maker Faire 2016 & Microsoft Booth
首先,感谢Hackster.io和微软,因为发表在Hackster.io的项目<A fall detection system based on Arduino, Windows and Azu ...
- 解决UDT中内存下不去的问题
使用UDT库,编写简单的网络通信程序,发现了一个问题,关闭一部分连接后,程序占用内存并没有变化. 比如先连接500个,再连接另500个,先关掉后面500个,程序占用内存降一半,再关 ...
- css div中内容绝对居中(多行内容)
div中的内容绝对居中(不适合IE6哦,IE6我已经不考虑了),直接看代码吧. <!DOCTYPE HTML> <html> <head> <title> ...
- Redis学习资源
1 redis官方网站 http://redis.io/ 2 redis中文 http://redisdoc.com/ 3 redis的设计与实现 http://www.redisbook.com/ ...