前言

await与async是C#5.0推出的新语法,关于await与async有很多文章讲解。但看完后有没有这样一种感觉,感觉这东西像是不错,但好像就是看不太懂,也不清楚该怎么使用。虽然偶有接触,但是一直都没有真正搞明白。

我也是才刚刚摸索明白,把学习结果和大家探讨一下看掌握得对不对。个人的学习习惯就是,有复杂的东西可以简单说明白,就会分享出来~

(阅读本文需要具备多线程及任务编程的基础)

 

重点

在学习async/await最难的是什么呢?就是理解它的工作方式!

1.所有的async方法返回类型必然是Task或Task<T>,这是异步处理的基础!

2.在async方法中遇到await关键字后,当前线程立即返回(到调用方),继续之前的处理逻辑;await关键字之后的代码逻辑,将交由新的线程处理;当新的线程处理完成后,可以从新的线程返回处理结果到调用(处)线程当中,结束等待。

3.在一个async方法中,会根据await关键字进行分割,拆分到不同的线程处理同一个方法的不同部分!

4.把一个方法代码的不同部分拆分到多个线程处理,这是异步方法和同步方法的最大不同!

把上面几点搞明白了,其实异步编程也就大概清楚了吧。。

 

简单异步调用

    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("{0}->Main.异步方法执行前", Thread.CurrentThread.ManagedThreadId.ToString());//输出异步处理之前的线程ID
            DoAsync(1000).Wait();//执行异步处理,并等待该异步方法执行完成后才继续
            Console.WriteLine("{0}->Main.异步方法执行后", Thread.CurrentThread.ManagedThreadId.ToString());//输出异步处理之后的线程ID

            Console.Read();
        }

        /// <summary>
        /// 执行异步处理
        /// </summary>
        /// <param name="times">模拟处理时长</param>
        /// <returns></returns>
        public static async Task DoAsync(int times)
        {
            Console.WriteLine("{0}->DoAsync.await之前", Thread.CurrentThread.ManagedThreadId.ToString());//输出调用线程ID
            await Task.Run(() => Thread.Sleep(times));///执行一个异步任务,并等待返回结果才继续;需要注意的是,调用线程执行到这一行的时候其实就已经返回了
            Console.WriteLine("{0}->DoAsync.await之后", Thread.CurrentThread.ManagedThreadId.ToString());//异步操作执行完了,但这里已经是新的线程了
        }
    }
1->Main.异步方法执行前
1->DoAsync.await之前
3->DoAsync.await之后
1->Main.异步方法执行后

请留意异步方法DoAsync的处理,在await之前和await之后,已经是两个不一样的线程ID。

也就是说,一个方法内部被拆分成了多个部分,不同的部分分别由不同的线程处理。

有返回值的异步调用

    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("{0}->Main.异步方法执行前", Thread.CurrentThread.ManagedThreadId.ToString());//输出异步处理之前的线程ID
            var asyncTask = DoAsync(1000);//异步执行处理
            Console.WriteLine("{0}->Main.异步方法执行后", Thread.CurrentThread.ManagedThreadId.ToString());//输出异步处理之后的线程ID
            var result = asyncTask.Result;//等待异步处理完成才继续
            Console.WriteLine("{0}->Main.异步方法完成后", Thread.CurrentThread.ManagedThreadId.ToString());//输出异步完成之后的线程ID

            Console.Read();
        }

        /// <summary>
        /// 执行异步处理
        /// </summary>
        /// <param name="times">模拟处理时长</param>
        /// <returns></returns>
        public static async Task<int> DoAsync(int times)
        {
            Console.WriteLine("{0}->DoAsync.await之前", Thread.CurrentThread.ManagedThreadId.ToString());//输出调用线程ID
            var result = await Task.Run(() => { Thread.Sleep(times); return times; });///执行一个异步任务,并等待返回结果才继续;需要注意的是,调用线程执行到这一行的时候就已经返回了
            Console.WriteLine("{0}->DoAsync.await之后", Thread.CurrentThread.ManagedThreadId.ToString());//异步操作执行完了,但这里已经是新的线程了
            return result;//返回计算结果,注意:返回结果时和进入方法时的线程已经不一样了
        }
    }
1->Main.异步方法执行前
1->DoAsync.await之前
1->Main.异步方法执行后
3->DoAsync.await之后
1->Main.异步方法完成后

请注意:在同步方法Main中执行的时候都是同一个线程;在异步方法DoAsync执行的时候,在await之前是调用线程,在await之后则是另一个线程。

 

总结

在异步(async)方法执行中,会根据await关键字,拆分一个方法为多个部分,分别由不同的线程执行。

在异步(async)方法执行中,遇到await关键字,调用线程会立即返回(线程池)继续后续的处理逻辑;而后,调用方可以使用Task.Wait()或Task<T>.Result进行阻塞,等待异步方法执行完毕再继续。

在异步(async)方法执行后,若不使用Task.Wait()进行等待,或不使用Task<T>.Result获取返回结果,则该方法将仅以异步方式执行。

 

那么异步方法的好处到底在哪?一下子,我也说不上来。。因为好像只用Task,也可以达到类似的效果,虽然也有区别。

难道,异步只是一颗语法糖吗?规范了异步方法的写法,回调函数也“消失”了。

其中异步方法最令人意外的,大概就是调用线程遇到await关键字,不用等待方法执行完毕就已经立即返回了吧!

同步夹杂着异步,异步夹杂着任务,异步方法里再拆分线程处理。想用好异步不容易!

 

参考

C#语法——await与async的正确打开方式

为什么我们要使用Async、Await关键字

async & await 的前世今生(Updated)

异步编程 In .NET

posted @ 2020-01-12 21:26 弎吩锺熱℃ 阅读(1023) 评论(6) 推荐(0) 编辑
摘要: C#使用互斥量(Mutex)实现多进程并发操作时多进程间线程同步操作(进程同步)的简单示例代码及使用方法。代码经过测试,可供参考,也可直接使用。 阅读全文
posted @ 2017-10-22 22:15 弎吩锺熱℃ 阅读(10075) 评论(6) 推荐(4) 编辑
摘要: 文件的并发写入问题,需要用到线程同步。而微软也给进程同步提供了一些相关的类可以达到这样的目的,本文使用到的 System.Threading.ReaderWriterLockSlim 便是其中之一,该类用于管理资源访问的锁定状态,可实现多线程读取或进行独占式写入访问。 阅读全文
posted @ 2016-12-11 16:24 弎吩锺熱℃ 阅读(29340) 评论(24) 推荐(23) 编辑
摘要: ASP.NET中使用DropDownList实现无刷新二级联动详细过程 阅读全文
posted @ 2015-01-04 17:18 弎吩锺熱℃ 阅读(3800) 评论(0) 推荐(1) 编辑
摘要: 使用innerHTML获取HTML代码时,HTML标记属性的双引号好多都消失不见了。原来是属性值中包含空格才会保留双引号,否则不保留。 阅读全文
posted @ 2014-11-14 07:40 弎吩锺熱℃ 阅读(2157) 评论(0) 推荐(0) 编辑
摘要: 在SQLServer中,根据不同的前缀,生成多套不重复并且不冲突的流水号。 阅读全文
posted @ 2014-10-22 11:48 弎吩锺熱℃ 阅读(914) 评论(0) 推荐(0) 编辑
摘要: 在ASP.NET中,将文本输入框类型改为密码后,页面上密码框总是空白。查看html代码,发现是缺少了value属性,在代码中设置value属性即可。 阅读全文
posted @ 2014-10-16 17:08 弎吩锺熱℃ 阅读(582) 评论(0) 推荐(0) 编辑
摘要: UDP 相对 TCP 来说速度更快,因为不需要先连接后传输,也不确保数据能够完整发送,且可以进行广播。UdpClient 类封装有关创建到 Internet 的 UDP 连接的详细信息。 阅读全文
posted @ 2014-07-31 18:52 弎吩锺熱℃ 阅读(8510) 评论(0) 推荐(2) 编辑
摘要: 面向连接的TCP和面向非连接的UDP协议,“面向连接”就是在正式通信前必须要与对方建立起连接,“面向非连接”就是在正式通信前不必与对方建立连接。TCP协议能为应用程序提供可靠的通信连接,使计算机发出的字节流完整无误地发往网络上的其他计算机,对可靠性要求高的数据通信。UDP适用于一次只传送少量数据,允许计算机发出的字节流残缺不全地发往网络上的其他计算机,对可靠性要求不高的应用环境。 阅读全文
posted @ 2014-07-29 19:56 弎吩锺熱℃ 阅读(357) 评论(0) 推荐(0) 编辑
摘要: C#如何获取CPU处理器核心数量,包括物理处理器数量、核心数量和逻辑处理器数量。逻辑处理器数是可通过Environment类获取,但其他信息都是只可通过WMI获取。 阅读全文
posted @ 2014-07-29 11:05 弎吩锺熱℃ 阅读(3416) 评论(0) 推荐(0) 编辑
点击右上角即可分享
微信分享提示