在.net中,async/await的理解

devtools/2025/3/6 15:56:15/

一、什么是同步?什么是异步?

.net中,async 和 await 是两个关键字,async 关键字用于声明一个方法是异步方法,该方法可以包含一个或多个 await 表达式。await 关键字是用于在异步方法中等待一个任务(Task 或 Task<T>对象)的完成。在 async 方法中使用 await表达式时,会暂停当前方法的执行,直到等待的任务完成。在这段时间内,主线程可以去执行其它操作。

什么是同步:当一个方法被调用时,调用者需要等待该方法执行完毕后才会继续往下执行,我们称这种方法为同步方法。

什么是异步:当一个方法被调用时立即返回,并获取一个线程执(Task)行该方法内部的业务逻辑,而调用者不需要等待这个方法执行完毕,我们称这种方法为异步方法。

二、async/await是怎么提高性能的?

异步的好处在于非阻塞(调用线程不会暂停执行去等待子线程完成),因此,我们可以把一些不需要立即使用结果、耗时的任务设为异步去执行,可以提高程序的执行效率。

比如,一个主线程需要执行 5 个方法,假设每个方法的分别用时为 0.1s、0.2s、0.3s、0.4s、0.5s,如果在同步编程中,这个主线程的执行用时大概为1.5秒。而如果把这5个方法写成异步的形式,那么这个主线程大概用时为0.5秒。

这是为什么呢?这是因为,在同步方法中,主线程在调用方法时,需要把这个方法执行完成之后再继续调用后续的方法,主线程执行这5个方法就好比一根线穿5颗珠子一样,一颗一颗来,所以主线程执行所用时间大概为1.5秒。而在异步方法中,主线程在调用异步方法时,主线程不会立即去执行异步方法的,而是在遇到异步方法中的 await语句后返回一个任务(Task),然后再继续调用后续的方法,这些任务的执行是由 task创建一个子线程去执行的,主线程执行这5个异步方法就好比5个人拿5根线同时穿5颗珠子,所用时间就是用时最多的那个,所以主线程执行时间大概为0.5秒。

问题:怎么解决在调用异步函数时,主线程继续向下执行后,主线程是怎么回收或管理这个异步方法的执行结果的。

1、await等待执行

public class Demo
{public async Task DemoAsync(){await Task.Delay(1000);Console.WriteLine("1秒后执行");}
}
public Class Program
{static async Task Main(string[] args){Demo demo = new Demo();// 使用 await 等待DemoAsync的执行await demo.DemoAsync();Console.ReadKey();}
}

2、使用事件

internal class Program
{static void Main(string[] args){Demo demo = new Demo();demo.OnEvent += (() =>{Console.WriteLine("事件订阅");});demo.DemoAsync();Console.ReadKey();}
}
public class Demo
{public event Action OnEvent;public async Task DemoAsync(){Console.WriteLine("开始执行");await Task.Delay(1000);Console.WriteLine("1秒后执行");OnEvent?.Invoke();}
}

3、回调函数

internal class Program{static void Main(string[] args){Demo demo = new Demo();demo.DemoAsync(() =>{Console.WriteLine("回调函数");});Console.ReadKey();}
}
public class Demo
{public async Task DemoAsync(Action callback){Console.WriteLine("开始执行");await Task.Delay(1000);callback?.Invoke();Console.WriteLine("1秒后执行");}
}

4、使用异步方法但不等待结果

这种方法知识启动了异步操作而不需要结果,可以简单的调用异步方法而不使用 await。这种方式不会阻塞主线程,也不会处理异步操作的结果。

internal class Program
{static void Main(string[] args){Demo demo = new Demo();demo.DemoAsync();Console.ReadKey();}
}
public class Demo
{public async Task DemoAsync(){Console.WriteLine("开始执行");await Task.Delay(1000);Console.WriteLine("1秒后执行");}
}

5、将异步结果存储在变量中

如果想要在某个时刻获取到异步操作中的结果,可以将异步操作的结果存储在变量中,然后再访问它。

internal class Program
{static void Main(string[] args){Demo demo = new Demo();var demoResult = demo.DemoAsync();// 使用 wait等待异步的完成demoResult.Wait();if (demoResult.IsCompleted){Console.WriteLine("str执行完成");Console.WriteLine(demoResult.Result);}Console.ReadKey();}
}
public class Demo
{public async Task<string> DemoAsync(){Console.WriteLine("开始执行");await Task.Delay(1000);Console.WriteLine("1秒后执行");return "异步返回结果";}
}

三、异步到底解决了什么?到底起到了什么样的作用?

1、提高响应性:使用 async 和 await 可以避免等待长时间运行的操作(如 IO 操作)阻塞主线程,从而提高应用程序的响应性。

2、简化异步操作:async 和 await 使得编写异步代码更接近同步代码的写法,这降低了异步编程的复杂性和出错的概率。

3、优化资源使用:异步操作允许线程在等待任务完成时释放,这样可以为其它任务腾出资源,而不是处于空闲等待状态。

四、在使用异步时的一些问题的解决

1、异步的传递性问题

异步方法的异步效果会在调用链上向上传递的,导致异步方法的调用链上的一些列方法也会被标记为异步。这种情况通常发生在下面场景中:

  1. 调用异步方法:如果一个方法调用了一个异步方法,那么这个方法也需要标记为 async,并使用 await 等待结果。
  2. 返回类型的变化:异步方法通常以 Task 或 Task<T> 类型返回,这意味着调用这些异步方法的其他方法也需要更新其返回类型以匹配 Task 或 Task<T> 。

异步方法的传递性的避免:

1、不使用 await 调用异步函数:如果在调用异步函数时,不需要等待异步函数的返回结果,那么可以不适用 await关键字调用异步函数。

2、使用 Task.Run 来启动异步操作:Task.Run 方法可以用来启动一个新的异步操作,它会提供一个新的任务来异步函数,从而避免了异步的传递性。

3、使用事件或回调来处理异步的结果:如果异步函数需要通知调用者操作已完成,可以使用事件或回调来代替直接的 await 调用。

4、将异步结果存放在变量中:如果需要等待异步操作的结果,但又不想立即等待它,可以将异步任务存储在变量中,然后在需要访问时使用 wait 方法来等待异步的完成。


好记性不然烂笔头,在学习的路上留下点痕迹。希望能给大家带来帮助,也期待你的点赞和讨论。

若有不足之处,还请斧正。


http://www.ppmy.cn/devtools/165025.html

相关文章

C++并发以及多线程的秘密

1.基础概念 并发&#xff08;Concurrency&#xff09; 并发是指在同一时间段内&#xff0c;多个任务看起来像是同时执行的。并发并不一定意味着真正的同时执行&#xff0c;它可以是通过时间片轮转等方式在多个任务之间快速切换&#xff0c;让用户感觉多个任务在同时进行。并发…

如何用AI完成多源异构数据集成

未来是AI的时代&#xff0c;我们用什么来和AI竞争呢&#xff0c;软考证书&#xff1f;哈哈&#xff0c;以前没考过&#xff0c;最近考几个备着&#xff0c;说不定管用。但我想说的是&#xff0c;IT思维最重要&#xff0c;而不管是系分还是架构&#xff0c;都是在培养IT思维。 …

【Liunx专栏_3】Liunx进程概念知识点

文章目录 前言1、冯诺依曼体系结构2、操作系统2.1、系统调用 3、进程3.1、进程概念3.2、进程描述—PCB3.3、查看进程信息3.4、通过系统调用获取进程标识符3.5、通过系统调用创建子进程—fork() 4、进程状态5、僵尸进程6、孤儿进程7、进程优先级7.1、PRI和NI是什么&#xff1f;7…

自定义wordpress三级导航菜单代码

首先&#xff0c;在你的主题functions.php文件中&#xff0c;添加以下代码以注册一个新的菜单位置&#xff1a; function mytheme_register_menus() {register_nav_menus(array(primary-menu > __(Primary Menu, mytheme))); } add_action(init, mytheme_register_menus); …

广域互联网关键技术详解(GRE/LSTP/IPsec/NAT/SAC/SPR)

《广域互联网关键技术详解》属于博主的“广域网”专栏&#xff0c;若想成为HCIE&#xff0c;对于广域网相关的知识需要非常了解&#xff0c;更多关于广域网的内容博主会更新在“广域网”专栏里&#xff0c;请持续关注&#xff01; 一.前言 广域互联技术纷杂多样&#xff0c;不…

Java-servlet(二)Java-servlet-Web环境搭建(上)IDEA,maven和tomcat工具下载(附Gitee直接下载)

Java-servlet&#xff08;二&#xff09;Java-servlet-Web环境搭建&#xff08;上&#xff09;IDEA&#xff0c;maven和tomcat工具下载 前言一、IDEA 的下载二、Maven 的下载我的gitee直接下载链接 三、Tomcat 的下载我的gitee直接下载链接 前言 上一节我们深入探讨了 Java-se…

【JavaScript】《JavaScript高级程序设计 (第4版) 》笔记-附录C-JavaScript 库和框架

附录C、JavaScript 库和框架 JavaScript 库和框架 JavaScript 库帮助弥合浏览器之间的差异&#xff0c;能够简化浏览器复杂特性的使用。库主要分两种形式&#xff1a;通用和专用。通用 JavaScript 库支持常用的浏览器功能&#xff0c;可以作为网站或 Web 应用程序开发的基础。专…

element-ui tooltip 组件源码分享

tooltip 组件源码分享&#xff0c;主要从以下两个方面&#xff1a; 1、tooltip 组件页面结构。 2、tooltip 组件属性。 一、组件页面结构。 二、组件属性。 2.1 effect 默认提供的主题&#xff0c;类型为 string&#xff0c;可选值有 dark/light&#xff0c;默认 dark。 2.…