新学.net,最近用到了ABP框架,发现了以下代码:api
public override async Task<UserDto> Get(EntityDto<long> input) { var user = await base.Get(input); var userRoles = await _userManager.GetRolesAsync(user.Id); user.Roles = userRoles.Select(ur => ur).ToArray(); return user; }
看到后,对async , Task , await 彻底不理解,就查阅了相关资料。服务器
简单说一下个人理解,这3个关键字都是多线程机制的一部分,目的是让处理用户请求的线程尽快结束这次请求,结束后,就能够用这个线程继续接收其余的请求。多线程
而费时的异步操做,交给后台线程处理(这里的后台线程,应该不能响应用户请求)。这样一来,就能让服务器更快地响应请求。app
Task对象中封装着线程相关的对象,async 和 await 都是为Task服务。 想在函数中使用await,就必须声明函数为async的。这是由于这里的await,理解为挂起更为恰当,当遇到await后,所在的函数执行必须挂起并当即返回主函数,而等待异步处理的结果得出后,继续执行接下来的代码。这样的函数是特殊的,须要一个async来标识。框架
而这里的Task<UserDto> 能够理解为,一个可以返回UserDto结果的,线程任务。Abp外层框架应该会对其进行await形式的调用,把结果返回给前台。异步
关于await 和 async 的理解,我贴2段代码,这2段代码是在搜资料时,从网上找到的,我对其进行了改进,感谢原做者的付出!async
第一段ide
using System; using System.Threading; using System.Threading.Tasks; namespace ConsoleApp2 { class Program { static void Main(string[] args) { Console.WriteLine("Main start-----Current Thread Id :{0}", Thread.CurrentThread.ManagedThreadId); Test(); Console.WriteLine("-----Current Thread Id :{0}", Thread.CurrentThread.ManagedThreadId); Console.WriteLine("-----Main end:{0}", Thread.CurrentThread.ManagedThreadId); Console.ReadLine(); } static async Task Test() { Console.WriteLine("======Current Thread Id :{0}", Thread.CurrentThread.ManagedThreadId); // 方法打上async关键字,就能够用await调用一样打上async的方法 await GetName(); Console.WriteLine("Middle---------Current Thread Id :{0}", Thread.CurrentThread.ManagedThreadId); await GetName2(); Console.WriteLine("##########Current Thread Id :{0}", Thread.CurrentThread.ManagedThreadId); } static async Task GetName() { Console.WriteLine("*****Current Thread Id :{0}", Thread.CurrentThread.ManagedThreadId); // Delay 方法来自于.net 4.5 // await Task.Delay(3000); // 返回值前面加 async 以后,方法里面就能够用await了 await Task.Run(() => { Thread.Sleep(3000); Console.WriteLine("^^^^^^^^Current Thread Id :{0}", Thread.CurrentThread.ManagedThreadId); }); Console.WriteLine("$$$$$$$Current Thread Id :{0}", Thread.CurrentThread.ManagedThreadId); } static async Task GetName2() { Console.WriteLine("!!!!!Current Thread Id :{0}", Thread.CurrentThread.ManagedThreadId); await Task.Run(() => { Thread.Sleep(3000); Console.WriteLine("++++++++Current Thread Id :{0}", Thread.CurrentThread.ManagedThreadId); }); Console.WriteLine("~~~~~~Current Thread Id :{0}", Thread.CurrentThread.ManagedThreadId); } } }
输出以下:函数
Main start-----Current Thread Id :1 ======Current Thread Id :1 *****Current Thread Id :1 -----Current Thread Id :1 -----Main end:1 ^^^^^^^^Current Thread Id :3 $$$$$$$Current Thread Id :3 Middle---------Current Thread Id :3 !!!!!Current Thread Id :3 ++++++++Current Thread Id :4 ~~~~~~Current Thread Id :4 ##########Current Thread Id :4
上面这段代码,能够明显地看 代码运行的线程,await会对被它阻塞的代码的运行线程产生影响!spa
第二段代码:
using System; using System.Threading; using System.Threading.Tasks; namespace ConsoleApp1 { class Program { static void Main(string[] args) { Console.WriteLine("我是主线程,线程ID:{0}", Thread.CurrentThread.ManagedThreadId); TestAsync(); Console.WriteLine("-----------------TestAsync 返回--------------------------"); Console.ReadLine(); } //为了在函数体内使用await,必须写async,表示这是个异步函数。 //而这里使用 await 的做用,就是能让 TestAsync 函数当即返回main函数,去执行main以后的逻辑,而不用等待 await 的task.在await的task执行完毕后,继续执行当前task。 //对于一个async函数,可使用await 去等待结果返回,也能够不使用,就像main函数这样,实现真正的异步效果,直接运行到了函数结尾。 static async Task TestAsync() { Console.WriteLine("调用GetReturnResult()以前,线程ID:{0}。当前时间:{1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss")); //建立task,但没有等待 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.Result, DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss")); //不会阻塞上层调用的写法 //这个await 找到最终的那个 新线程await,并 跳过等待结果,直接返回 Console.WriteLine("获得GetReturnResult()方法的结果:{0}。当前时间:{1}", await name, DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss")); Console.WriteLine("await 后面的逻辑 => TestAsync 最后一句"); } static Task<string> GetReturnResult() { Console.WriteLine("执行Task.Run以前, 线程ID:{0}", Thread.CurrentThread.ManagedThreadId); return Task.Run(() => { Console.WriteLine("并行线程:GetReturnResult()方法里面线程ID: {0}", Thread.CurrentThread.ManagedThreadId); Console.WriteLine("并行线程:running...."); Thread.Sleep(3000); return "我是返回值"; }); } } }
结果以下:
我是主线程,线程ID:1 调用GetReturnResult()以前,线程ID:1。当前时间:2018-06-28 03:06:32 执行Task.Run以前, 线程ID:1 调用GetReturnResult()以后,线程ID:1。当前时间:2018-06-28 03:06:32 -----------------TestAsync 返回-------------------------- 并行线程:GetReturnResult()方法里面线程ID: 3 并行线程:running.... 获得GetReturnResult()方法的结果:我是返回值。当前时间:2018-06-28 03:06:35 await 后面的逻辑 => TestAsync 最后一句
上面这段代码,能够看出async 和await 的具体效果: 主函数直接调用一个用async声明的函数(不在前面加await),运行到async声明的函数中的await后当即返回主函数,不会阻塞主函数的代码块。这个函数返回的task,通常都会和一个正在后台线程相关。
这里须要注意一点,先看代码:
static Task<string> GetReturnResult() { Console.WriteLine("执行Task.Run以前, 线程ID:{0}", Thread.CurrentThread.ManagedThreadId); var task = new Task<string>(() => { Console.WriteLine("并行线程:GetReturnResult()方法里面线程ID: {0}", Thread.CurrentThread.ManagedThreadId); Console.WriteLine("并行线程:running...."); Thread.Sleep(3000); return "我是返回值"; }); //用new建立的task对象,必须使用start方法开始任务,否则task不会开始,在这里就会会永久阻塞程序。 task.Start(); return task; /* return Task.Run(() => { Console.WriteLine("并行线程:GetReturnResult()方法里面线程ID: {0}", Thread.CurrentThread.ManagedThreadId); Console.WriteLine("并行线程:running...."); Thread.Sleep(3000); return "我是返回值"; }); */ }
虽然这个函数咱们仅仅须要Task<string>,可是其实一个task必需要start后,才可能被启动执行。而Task.Run 是自动调用start方法的。
这里再帖一份代码,是使用owin中间件进行重定向的代码,其中有3种对task的使用方法:
app.MapWhen((r) => !r.Request.Path.Value.ToLowerInvariant().StartsWith("/api") && !r.Request.Path.Value.ToLowerInvariant().StartsWith("/swagger") && !r.Request.Path.Value.ToLowerInvariant().Equals("/") && !r.Request.Path.Value.ToLowerInvariant().Equals("") , spa => { spa.Run(context => { //first //context.Response.Redirect("/people");
//虽然是同步逻辑,可是须要一个异步结构的返回值,使用Task.FromResult<>能够构造 //return Task.FromResult<int>(0); //second //return Task.Run(() => context.Response.Redirect("/people")); //third var task = new Task(() => { context.Response.Redirect("/people"); }); //注意,必须手动启动!!!!!!! task.Start(); return task; }); });