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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | /// <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; //这句话必须由主线程来执行,线程在同一时刻只能做一件事儿 } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | /// <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 } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | 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; //这句话必须由主线程来执行,线程在同一时刻只能做一件事儿 -----(主线程在等待)---- } |
测试用例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 | 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 } |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现