理解:
1、async修饰的方法可理解为异步方法(必须要配合await,否则和普通方法无异)
2、当async方法执行遇到await,则立即将控制权转移到async方法的调用者
3、由调用者决定是否需要等待async方法执行完再继续往下执行
4、await会挂起当前方法,即阻塞当前方法继续往下执行,转交控制权给调用者
注意:如果调用一个async方 法,却不使用await关键字来标记一个挂起点的话,程序将会忽略async关键字并以同步的方式执行。编译器会对类似的问题发出警告。
例子一:(控制台程序)
1 static void Main(string[] args) 2 { 3 MyMain();//由于main方法无法定义成async,顾此定义一个方法MyMain来表示main方法。 4 Console.Read(); 5 } 6 static async void MyMain() 7 { 8 Console.WriteLine("main方法开始执行"); 9 AsyncAction(); 10 Console.WriteLine("main方法继续执行"); 11 Console.WriteLine("main方法执行结束"); 12 } 13 14 static async Task<string> AsyncAction() 15 { 16 Console.WriteLine("AsyncAction方法await之前"); 17 string result = await Task<string>.Run(() => 18 { 19 Thread.Sleep(1000); 20 Console.WriteLine("AsyncAction方法sleep一秒后"); 21 return "AsyncAction的返回值"; 22 }); 23 Console.WriteLine("AsyncAction方法await之后-{0}", result); 24 return result; 25 }
执行结果:
由返回结果可以知,此异步方法AsyncAction执行遇到await,即程序执行到此行立刻被挂起,将控制权限交给MyMain方法,由MyMain决定是否等待AsyncAction执行完再往下执行,此处由于执行AsyncAction方法前不加await,所以直接往下执行。而AsyncAction方法中的await Task.Run(包括)后的代码会异步执行。
例子二:例子一中MyMain方法执行AsyncAction前面加await
1 static void Main(string[] args) 2 { 3 MyMain();//由于main方法无法定义成async,顾此定义一个方法MyMain来表示main方法。 4 Console.Read(); 5 } 6 static async void MyMain() 7 { 8 Console.WriteLine("main方法开始执行"); 9 await AsyncAction(); 10 Console.WriteLine("main方法继续执行"); 11 Console.WriteLine("main方法执行结束"); 12 } 13 14 static async Task<string> AsyncAction() 15 { 16 Console.WriteLine("AsyncAction方法await之前"); 17 string result = await Task<string>.Run(() => 18 { 19 Thread.Sleep(1000); 20 Console.WriteLine("AsyncAction方法sleep一秒后"); 21 return "AsyncAction的返回值"; 22 }); 23 Console.WriteLine("AsyncAction方法await之后-{0}", result); 24 return result; 25 }
执行结果:
由于AsyncAction方法执行前加了await,故MyMain方法要等待其执行结束才继续往下执行。
使用场景及用法分析:
当有某一操作执行时间较长或者执行结果对整体程序执行影响不大(一般此操作都会是一个独立一个方法。)主线程的执行不依赖于此操作的执行结果。
常见于一些IO操作,如日志的记录,当触发记录日志动作的时候,应该让其独立一个线程去执行,此时不管日志记录成功与否,
都不应该阻塞主线程的执行。
在上面的例子一中,可以将AsyncAction方法中的 Task.Run 当成记录日志的动作,当程序执行到 await Task.Run后,
AsyncAction方法立刻被挂起,主线程MyMain继续往下执行。
这里可能有人会疑问?为何不直接在主线程MyMain中直接开启线程 Task.Run 来记录日志?
而是要定义到一个异步方法(此例子为AsyncAction方法)中处理日志。
是,如果此时只是为了记录日志,也不是必须要写到一个方法中。但是,
首先,这种单一功能的操作,本应该就是独立写到一个方法中。
其次是 AsyncAction方法 中的 await Task.Run 的返回值也许还有其他操作。
如下面例子:记录日志之前,需要获取登陆用户的一些信息,并且将用户信息一同保存到日志中,如果写到主线程MyMain中,则避免不了阻塞主线程(因为需要等待获取用户信息)。
当然,你也可以将这两个方法一起写到一个方法中由Task调用,这里只是举个例子来说明Async和await的用法场景。
1 static void Main(string[] args) 2 { 3 MyMain();//由于main方法无法定义成async,顾此定义一个方法MyMain来表示main方法。 4 Console.Read(); 5 } 6 static async void MyMain() 7 { 8 Console.WriteLine("main方法开始执行"); 9 WriteUserLog(); 10 Console.WriteLine("main方法继续执行"); 11 Console.WriteLine("main方法执行结束"); 12 } 13 14 static async void WriteUserLog() 15 { 16 Console.WriteLine("获取用户信息之前"); 17 string userInfo = await GetUserInfoAsync(); //尽量早的使用await,以便此方法尽快挂起(异步执行) 18 Console.WriteLine("写入日志之前"); 19 WriteLog(userInfo); 20 } 21 22 static Task<string> GetUserInfoAsync() 23 { 24 return Task.Factory.StartNew(() => { 25 Thread.Sleep(1000); 26 Console.WriteLine("获取用户信息完成"); 27 return "jxf"; 28 }); 29 } 30 31 static void WriteLog(string userInfo) 32 { 33 Thread.Sleep(1000); 34 Console.WriteLine("写入日志完成,用户信息:{0}", userInfo); 35 }
执行结果: