Async Await 多线程等待 应用
不同框架的应用
///1.Winform--存在特殊处理
///2.ASP.NETCore---放心用
///3.控制台---放心用
///4.WPF----没试过---
///5.Core WebApi---放心用
Web开发 推荐
适用
跟第三方交互的(非托管资源,经常有async版本): 数据库openAsync-Redis Web请求-Api 文件读取 一用到底 Await为什么能提升吞吐—只负责发命令—然后就忙别的去了—不需 要等待---事儿完成前就不浪费资源---完成后再来线程处理---这里还 能复用
不适用
服务器本地计算(CPU密集型,托管资源 ) : 大数据加减乘除, 数据处理 反而可能影响性能 但是用了没啥事儿
用法
1 async 是用来修饰方法,如果单独出现,方法会警告,没有什么作用
2 await在方法体内部,只能放在async修饰的方法内,必须放在task前面
3 async/await方法里面如果没有返回值,默认返回一个Task,或者void(推荐用Task,而不是void,因为这 样才能await/wait)
4 带async+await后,返回值要多一层Task<>
硬件DMA技术, 带Async后缀的API 硬盘:接受命令—然后cpu忙自己的(线程)—写完/读完,会发中断信 号,CPU再继续处理,这就基于DMA异步操作,就是可以节约CPU 资源(线程)---- 线程启动-等着-当然很浪费 用了异步就不用等
4种等待方式
t.Wait()
Task.WaitAll()
t.Result
await t
Winform
/// <summary> ///异步方法: 正常执行 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private async void btnAsync_Click(object sender, EventArgs e) { Debug.WriteLine($"**********************************btnAsync_Click******************************************"); Debug.WriteLine($"This is btnAsync_Click Start,ThreadId={Thread.CurrentThread.ManagedThreadId}"); long lResult = await this.CalculationAsync(1_000_000); Debug.WriteLine($"This is btnAsync_Click End,ThreadId={Thread.CurrentThread.ManagedThreadId}"); this.textAsyncResult.Text = lResult.ToString(); //这句话必须要主线程来执行的 //更改控件的值,这里必须是(UI线程)主线程去执行;每次执行都能成功,说明每次这里都是主线程来执行的;跟Winform设计有关系;在Winform中,await后面的内容,都会让主线程来执行; } private async Task<long> CalculationAsync(long total) { var task = await Task.Run(() => { Debug.WriteLine($"This is CalculationAsync Start,ThreadId={Thread.CurrentThread.ManagedThreadId}"); long lResult = 0; for (int i = 0; i < total; i++) { lResult += i; } Debug.WriteLine($"This is CalculationAsync End,ThreadId={Thread.CurrentThread.ManagedThreadId}"); return lResult; }); return task; //这句话必须由主线程来执行,线程在同一时刻只能做一件事儿 }
/// <summary> /// 同步方法:界面卡死了 新加一个线程变成三个线程等待 就不会死锁了 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void btnSync_Click(object sender, EventArgs e) { Debug.WriteLine($"**********************************btnSync_Click******************************************"); Debug.WriteLine($"This is btnSync_Click Start,ThreadId={Thread.CurrentThread.ManagedThreadId}"); //var task = this.CalculationAsync(1_000_000); ////task.Wait(); //主线程阻塞等待 ////1.主线程要等待 Winform特殊设计:await后面的内容必须由主线程执行; ////2.主线程在这儿也等待着在 ////3.主线程无暇分身导致死锁 ////4.怎么解决这个死锁? //long lResult = task.Result;//主线程阻塞等待,主线程在等结果,经过分析可以确定,界面卡死问题肯定是出在这儿 long lResult = this.GetCalculationAsync(1_000_000); Debug.WriteLine($"This is btnSync_Click End,ThreadId={Thread.CurrentThread.ManagedThreadId}"); this.textSync.Text = lResult.ToString(); } private long GetCalculationAsync(long total) { var taskLong = Task.Run(() => { //新开一个线程 var task = this.CalculationAsync(total); long lResult = task.Result;//子线程 return lResult; }); return taskLong.Result;//主线程在等Result }
private void btnSync_Click(object sender, EventArgs e) { Debug.WriteLine($"**********************************btnSync_Click******************************************"); Debug.WriteLine($"This is btnSync_Click Start,ThreadId={Thread.CurrentThread.ManagedThreadId}"); var task = this.CalculationAsync(1_000_000); //task.Wait(); //主线程阻塞等待 ////1.主线程要等待 Winform特殊设计:await后面的内容必须由主线程执行; ////2.主线程在这儿也等待着在 ////3.主线程无暇分身导致死锁 ////4.怎么解决这个死锁? long lResult = task.Result;//主线程阻塞等待,主线程在等结果,经过分析可以确定,界面卡死问题肯定是出在这儿 ------等待---相互等待锁死 // long lResult = this.GetCalculationAsync(1_000_000); windform 中await后面必须由主线程完成 Debug.WriteLine($"This is btnSync_Click End,ThreadId={Thread.CurrentThread.ManagedThreadId}"); this.textSync.Text = lResult.ToString(); } private async Task<long> CalculationAsync(long total) { var task = await Task.Run(() => { Debug.WriteLine($"This is CalculationAsync Start,ThreadId={Thread.CurrentThread.ManagedThreadId}"); long lResult = 0; for (int i = 0; i < total; i++) { lResult += i; } Debug.WriteLine($"This is CalculationAsync End,ThreadId={Thread.CurrentThread.ManagedThreadId}"); return lResult; }); return task; //这句话必须由主线程来执行,线程在同一时刻只能做一件事儿 -----(主线程在等待)---- }
测试用例
public class AwaitAsyncTest { public static void Show() { #region ReadFile //ReadFile对比Task:当然并发---10个线程 Async: 可以并发,但是并发不多---只有3个线程 Sync:同步,按顺序执行 { Console.WriteLine("******************ReadFile***************"); string path = "D:\\ZXWork\\Advanced15\\20210713Advanced15Course05Delegate.rar"; int loopNum = 20; { Console.WriteLine("*****************Async****************"); List<Task> taskList = new List<Task>(); Stopwatch stopwatch = new Stopwatch(); stopwatch.Start(); for (int i = 0; i < loopNum; i++) { taskList.Add(ReadAsync(path, i)); } Task.WaitAll(taskList.ToArray()); stopwatch.Stop(); Console.WriteLine($"Async耗时{stopwatch.ElapsedMilliseconds}ms,ThreadId={Thread.CurrentThread.ManagedThreadId}"); } Thread.Sleep(3000); { Console.WriteLine("*****************Task****************"); List<Task> taskList = new List<Task>(); Stopwatch stopwatch = new Stopwatch(); stopwatch.Start(); for (int i = 0; i < loopNum; i++) { taskList.Add(ReadTask(path, i)); } Task.WaitAll(taskList.ToArray()); stopwatch.Stop(); Console.WriteLine($"Task耗时{stopwatch.ElapsedMilliseconds}ms,ThreadId={Thread.CurrentThread.ManagedThreadId}"); } Thread.Sleep(3000); //{ // Console.WriteLine("*****************Sync****************"); // Stopwatch stopwatch = new Stopwatch(); // stopwatch.Start(); // for (int i = 0; i < loopNum; i++) // { // ReadSync(path, i); // } // stopwatch.Stop(); // Console.WriteLine($"Sync耗时{stopwatch.ElapsedMilliseconds}ms,ThreadId={Thread.CurrentThread.ManagedThreadId}"); //} } #endregion #region InvokeWeb //InvokeWeb对比 Task:耗时长一些,并发不够高------10个线程---铁打的10个线程 Async:并发高,速度快----少于10个线程,没有影响并发,能重用就是没事儿了, 利用率高一些 Sync:串行的,耗时长 { Console.WriteLine("******************InvokeWeb***************"); string url = "http://localhost:8080/home/Sleep"; int loopNum = 10;//5 int second = 5; { Console.WriteLine("*****************Async****************"); List<Task> taskList = new List<Task>(); Stopwatch stopwatch = new Stopwatch(); stopwatch.Start(); for (int i = 0; i < loopNum; i++) { taskList.Add(WebAsync(url, i, second)); } Task.WaitAll(taskList.ToArray()); stopwatch.Stop(); Console.WriteLine($"Async耗时{stopwatch.ElapsedMilliseconds}ms,ThreadId={Thread.CurrentThread.ManagedThreadId}"); } Thread.Sleep(3000); { Console.WriteLine("*****************Task****************"); List<Task> taskList = new List<Task>(); Stopwatch stopwatch = new Stopwatch(); stopwatch.Start(); for (int i = 0; i < loopNum; i++) { taskList.Add(WebTask(url, i, second)); } Task.WaitAll(taskList.ToArray()); stopwatch.Stop(); Console.WriteLine($"Task耗时{stopwatch.ElapsedMilliseconds}ms,ThreadId={Thread.CurrentThread.ManagedThreadId}"); } Thread.Sleep(3000); { Console.WriteLine("*****************Sync****************"); Stopwatch stopwatch = new Stopwatch(); stopwatch.Start(); for (int i = 0; i < loopNum; i++) { WebSync(url, i, second); } stopwatch.Stop(); Console.WriteLine($"Sync耗时{stopwatch.ElapsedMilliseconds}ms,ThreadId={Thread.CurrentThread.ManagedThreadId}"); } } #endregion #region DoCalculation { Console.WriteLine("******************DoCalculation***************"); int loopNum = 10;//5 long total = 1_000_000_000; { Console.WriteLine("*****************Task****************"); List<Task> taskList = new List<Task>(); Stopwatch stopwatch = new Stopwatch(); stopwatch.Start(); for (int i = 0; i < loopNum; i++) { taskList.Add(DoCalculationTask(total, i)); } Task.WaitAll(taskList.ToArray()); stopwatch.Stop(); Console.WriteLine($"Task耗时{stopwatch.ElapsedMilliseconds}ms,ThreadId={Thread.CurrentThread.ManagedThreadId}"); } Thread.Sleep(3000); { Console.WriteLine("*****************Async****************"); List<Task> taskList = new List<Task>(); Stopwatch stopwatch = new Stopwatch(); stopwatch.Start(); for (int i = 0; i < loopNum; i++) { taskList.Add(DoCalculationAsync(total, i)); } Task.WaitAll(taskList.ToArray()); stopwatch.Stop(); Console.WriteLine($"Async耗时{stopwatch.ElapsedMilliseconds}ms,ThreadId={Thread.CurrentThread.ManagedThreadId}"); } Thread.Sleep(3000); { Console.WriteLine("*****************Sync****************"); Stopwatch stopwatch = new Stopwatch(); stopwatch.Start(); for (int i = 0; i < loopNum; i++) { DoCalculationSync(total, i); } stopwatch.Stop(); Console.WriteLine($"Sync耗时{stopwatch.ElapsedMilliseconds}ms,ThreadId={Thread.CurrentThread.ManagedThreadId}"); } } #endregion } #region Read //await后面也会开启线程---只要其中一个执行结束了,当前这个现场马上可能去执行其他的人 private static async Task<byte[]> ReadAsync(string path, int num) { Console.WriteLine($"This is ReadAsync{num} Start,ThreadId={Thread.CurrentThread.ManagedThreadId}"); var result = await File.ReadAllBytesAsync(path); //这里读取文件:文件--比较大,也是耗好性能--计算机肯定回卡,主线程到这儿相当于说是告诉帮助类库,要做什么事儿,主线程就跑了,在这类并没有开启新的线程---降低了线程的开启的数量; 也降低了CPU的负荷; 降低了服务器的运行负荷 Console.WriteLine($"This is ReadAsync{num} End,ThreadId={Thread.CurrentThread.ManagedThreadId}"); return result; } //铁定10个线程 private static Task<byte[]> ReadTask(string path, int num) { Console.WriteLine($"This is ReadTask{num} Start,ThreadId={Thread.CurrentThread.ManagedThreadId}"); var result = Task.Run(() => { Console.WriteLine($"This is ReadTask Ing,ThreadId={Thread.CurrentThread.ManagedThreadId}"); return File.ReadAllBytes(path); }); Console.WriteLine($"This is ReadTask{num} End,ThreadId={Thread.CurrentThread.ManagedThreadId}"); return result; } private static byte[] ReadSync(string path, int num) { Console.WriteLine($"This is ReadSync{num} Start,ThreadId={Thread.CurrentThread.ManagedThreadId}"); var result = File.ReadAllBytes(path); Console.WriteLine($"This is ReadSync{num} End,ThreadId={Thread.CurrentThread.ManagedThreadId}"); return result; } #endregion #region InvokeWebRequest private static string InvokeWebRequest(string url) { string html = null; try { HttpWebRequest request = HttpWebRequest.Create(url) as HttpWebRequest;//模拟请求 request.Timeout = 30 * 1000;// using (HttpWebResponse response = request.GetResponse() as HttpWebResponse)//发起请求 { if (response.StatusCode != HttpStatusCode.OK) { Console.WriteLine($"抓取{url}地址返回失败,response.StatusCode为{response.StatusCode}"); } else { StreamReader sr = new StreamReader(response.GetResponseStream()); html = sr.ReadToEnd();//读取数据 } } } catch (WebException ex) { if (ex.Message.Equals("远程服务器返回错误: (306)。")) { Console.WriteLine($"抓取{url}地址返回失败,远程服务器返回错误: (306)"); html = null; } } catch (Exception ex) { Console.WriteLine($"抓取{url}地址返回失败,远程服务器返回错误{ex.Message}"); html = null; } return html; } private static async Task<string> InvokeWebRequestAsync(string url) { string html = null; try { HttpWebRequest request = HttpWebRequest.Create(url) as HttpWebRequest;//模拟请求 request.Timeout = 30 * 1000;// //request.GetResponseAsync异步版本的发起请求 using (HttpWebResponse response = (await request.GetResponseAsync()) as HttpWebResponse)//发起请求 { if (response.StatusCode != HttpStatusCode.OK) { Console.WriteLine($"抓取{url}地址返回失败,response.StatusCode为{response.StatusCode}"); } else { StreamReader sr = new StreamReader(response.GetResponseStream()); return await sr.ReadToEndAsync();//异步 } } } catch (WebException ex) { if (ex.Message.Equals("远程服务器返回错误: (306)。")) { Console.WriteLine($"抓取{url}地址返回失败,远程服务器返回错误: (306)"); html = null; } } catch (Exception ex) { Console.WriteLine($"抓取{url}地址返回失败,远程服务器返回错误{ex.Message}"); html = null; } return html; } private static async Task<string> WebAsync(string url, int num, int second) { url = $"{url}?loop={num}&type=Async&second={second}"; Console.WriteLine($"This is InvokeAsync{url} Start,ThreadId={Thread.CurrentThread.ManagedThreadId}"); var result = await InvokeWebRequestAsync(url); Console.WriteLine($"This is InvokeAsync{url} End,ThreadId={Thread.CurrentThread.ManagedThreadId}"); return result; } private static Task<string> WebTask(string url, int num, int second) { url = $"{url}?loop={num}&type=Task&second={second}"; Console.WriteLine($"This is WebTask{num} Start,ThreadId={Thread.CurrentThread.ManagedThreadId}"); var result = Task.Run(() => { Console.WriteLine($"This is WebTask Ing,ThreadId={Thread.CurrentThread.ManagedThreadId}"); return InvokeWebRequest(url); }); Console.WriteLine($"This is WebTask{num} End,ThreadId={Thread.CurrentThread.ManagedThreadId}"); return result; } private static string WebSync(string url, int num, int second) { url = $"{url}?loop={num}&type=Sync&second={second}"; Console.WriteLine($"This is WebSync{num} Start,ThreadId={Thread.CurrentThread.ManagedThreadId}"); var result = InvokeWebRequest(url); Console.WriteLine($"This is WebSync{num} End,ThreadId={Thread.CurrentThread.ManagedThreadId}"); return result; } #endregion #region DoCalculation CPU密集型 private static long Calculation(long total) { long lResult = 0; for (int i = 0; i < total; i++) { lResult += i; } return lResult; } private static async Task<long> CalculationAsync(long total) { return await Task.Run(() => { long lResult = 0; for (int i = 0; i < total; i++) { lResult += i; } return lResult; }); } private static async Task<long> DoCalculationAsync(long total, int num) { Console.WriteLine($"This is DoCalculationAsync{num} Start,ThreadId={Thread.CurrentThread.ManagedThreadId}"); var result = await CalculationAsync(total); Console.WriteLine($"This is DoCalculationAsync{num} End,ThreadId={Thread.CurrentThread.ManagedThreadId}"); return result; } private static Task<long> DoCalculationTask(long total, int num) { Console.WriteLine($"This is DoCalculationTask{num} Start,ThreadId={Thread.CurrentThread.ManagedThreadId}"); var result = Task.Run(() => { Console.WriteLine($"This is DoCalculationTask Ing,ThreadId={Thread.CurrentThread.ManagedThreadId}"); return Calculation(total); }); Console.WriteLine($"This is DoCalculationTask{num} End,ThreadId={Thread.CurrentThread.ManagedThreadId}"); return result; } private static long DoCalculationSync(long total, int num) { Console.WriteLine($"This is DoCalculationSync{num} Start,ThreadId={Thread.CurrentThread.ManagedThreadId}"); var result = Calculation(total); Console.WriteLine($"This is DoCalculationSync{num} End,ThreadId={Thread.CurrentThread.ManagedThreadId}"); return result; } #endregion }