微信公众号:架构师高级俱乐部
关注可了解更多的编程,架构知识。问题或建议,请公众号留言;
如果你觉得此文对你有帮助,欢迎转发

在.NET中执行异步/等待的两种错误方法

在应用开发中,我们为了提高应用程序的吞吐能力或者异步操作来减少耗时,通常会使用多线程来达到目的,而在C#语言中由于async/await必杀技的存在,大多会使用此来简化多线程操作,async/await的具体使用方式想必大家已烂熟于心,不再赘述,今天主要谈谈在我们经常所谓的async/await操作真的是正确的吗?你又真的用对他了吗?

情景1:消除异步方法优势

请看下面代码示例

ServiceClient client = new ServiceClient();
ServiceRequest request = new ServiceRequest();
request.Id = newId;
var responseTask = Task.Run(() => client.GetServicesAsync(request));
ServiceResponse response = await responseTask;

以上代码片段在一个异步方法中,此方法在另一个Task中返回一个Task!(Task.Run) 这是多余的。如果该方法已经返回Task,则我们不应该将其包装在另一个Task中。因此,在这种情况下,解决方案也非常简单,取消一切不必要的Task:

ServiceClient client = new ServiceClient();
ServiceRequest request = new ServiceRequest();
request.Id = newId;
var responseTask = client.GetServicesAsync(request);
ServiceResponse response = await responseTask;

情景2:滥用Task.Run()

在工作中大多使用Task.Run的代码给人的印象是异步有助于提高性能。这是正确的,但仅是非常片面的。Async/Await的目的是帮助提高吞吐量。改善性能仅仅是副作用。

因此在工作中会发现各种奇奇怪怪的代码,例如以下为了配合外部异步方法,又由于内部各种原因没有实现异步方法,不得不用Task.Run来包裹同步方法而达到语法要求。

MyService client = new MyService();
var responseTask = Task.Run(() => client.GetData(request));
var response = await responseTask;

换句话说:使用Task.Run()包装了一个同步调用。

这里的问题是方法client.GetData()本身并不是异步方法,通过将异步包装器置于同步方法之上,我们正在做一个称为“async-over-sync异步超同步”的反模式,这在大多数情况下最终不是一个推荐的做法。不是因为它不起作用(而是起作用),而是因为它效率不高。

之所以如此,原因是很长的,而且涉及很多,如果感兴趣可在文章末尾找到Stephen作者相关对此问题的详细解释地址[1]。总结一下,以上代码非常糟糕,因为实现异步的好处是通过在线程不执行任何操作(例如,等待服务响应)时“释放”线程来提高吞吐量。上面的示例确实释放了一个线程,它也立即消耗了另一个线程来执行任务包装的代码,并且该消耗的线程在等待服务响应时被阻塞。因此,我们没有提高吞吐量,只是将工作从一个线程转移到了另一个线程。而且在并发下,以上使用方式在工作中也极大的降低了系统性能!


解决方案可以简化为:不要对同步方法使用异步包装器!只需同步调用它们即可。在这种情况下,理论上的性能优势将被潜在的问题所抵消,这些潜在的问题在最坏的情况下可能包括死锁。

过度使用Task.Run()有很大安全隐患,尤其在你未搞懂你写了什么的时候,这种影响在复杂业务和超大并发下出问题非常难排查!在发现性能严重影响又找不到原因的时候,请排查出所有使用Task.Run的代码,确定是否是以上两种情况,解决他们可能就海阔天空了

摘要

在.NET或者.Netcore中使用Async/Await都是一项技巧,必须谨慎使用。在上面的示例中,开发团队试图使他们的应用程序性能更好,但最终由于对他们的代码过度使用Async/Await而使情况变的难以控制。

总之应该记住两件事:

不要将异步任务包装在另一个异步包装器Task.Run中。

不要在同步调用上使用异步包装器。

有很多方法可以修正使用异步/等待的ASP.NET代码。


更多请参考

[1]:https://devblogs.microsoft.com/pfxteam/should-i-expose-asynchronous-wrappers-for-synchronous-methods/(我应该为同步方法加异步包装器Task.Run吗)

在.NET中执行Async/Await的两种错误方法相关推荐

  1. Json返回时间中出现乱码问题的两种解决方法

    Json返回时间中出现乱码问题的两种解决方法 参考文章: (1)Json返回时间中出现乱码问题的两种解决方法 (2)https://www.cnblogs.com/hanyinglong/archiv ...

  2. c++STL中的find()函数 有两种使用方法

    c++STL中的find()函数 有两种使用方法 方法一: 开头引头文件:中的函数 其调用形式为 find(start,end,value) start搜寻的起点,end搜寻的终点,要寻找的value ...

  3. JavaScript中的ReferenceError和TypeError两种错误的区别

    转自JavaScript中的ReferenceError和TypeError两种错误的区别 作为前端工作人员,在调试JavaScript程序的时候经常遇到两个错误:ReferenceError和Typ ...

  4. Java中关于Arrays.sort的两种重载方法的理解

    前言 在java中重载排序方法的方法目前有两种,一种是实现Comparable接口的compareTo方法,还有一种是用比较器(comparator) 作为参数,其中比较器是实现了Comparator ...

  5. 计算机一级无法打开office,电脑中无法运行Office2016的两种解决方法

    最近,有些用户在电脑中安装完office2016后打开该程序时总是提示:无法启动此程序,因为计算机中丢失 api-ms-win-crt-stdio-l1-1-0.dll .那么,遇到这问题该怎么来解决 ...

  6. 风控模型中特征重要度的两种筛选方法

    在采用决策树算法建立模型的场景中,例如GBDT.XGBoost.LightGBM.Random Forest等,我们习惯通过Feature Importance指标作为特征筛选的重要方法之一.从特征定 ...

  7. DB2 存储过程中执行动态SQL的两种写法

    样本代码: DROP PROCEDURE QUOTATION.COPY_SAMPLE; CREATE PROCEDURE QUOTATION.COPY_SAMPLE (IN tableNameFrom ...

  8. python timer使用-python中timer定时器常用的两种实现方法

    方法一,使用线程中现成的: 这种一般比较常用,特别是在线程中的使用方法,下面是一个例子能够很清楚的说明它的具体使用方法: #! /usr/bin/python3 #! -*- conding: utf ...

  9. mybatis中sql语句中大于小于号的两种解决方法

    1.使用<![CDATA[]]>,因为CDATA 部分中的所有内容都会被解析器忽略,所以建议使用<![CDATA[]]> 来解决小于号问题:实例:<![CDATA[ sq ...

最新文章

  1. Hive metastore三种配置方式
  2. 建立普通用户信任关系,
  3. [转] android 中 pinyin4j的使用
  4. HDU 2865 Birthday Toy [Polya 矩阵乘法]
  5. 推荐系统阅读清单:最近我们在读哪些论文?
  6. 介绍自己的一个Android插桩热修复框架项目QuickPatch
  7. 使用application log分析Fiori navigation target解析错误
  8. 火狐 增强查找工具栏_在“提示”框中:简单的IE至Firefox同步,轻松的Windows工具栏和识别USB电缆...
  9. 2017蓝桥杯决赛-发现环 数据结构|搜索
  10. Windows下Zookeeper启动zkServer.cmd闪退问题的解决方案
  11. 福建省计算机应用考试成绩,福建省高校学生计算机应用水平考试成绩查询
  12. python -m以模块方式启动,python命令加上-u(unbuffered)参数后会强制其标准输出
  13. ASP.NET 订餐系统-程序+配置文档
  14. 电子元件-继电器知识汇总
  15. python numpy库下载_python3.6下Numpy库下载与安装图文教程
  16. PDF文件怎么修改,PDF文件修改的方法
  17. php to es7,只需五步 集成新版 Elasticsearch7.9 中文搜索 到你的 Laravel7 项目
  18. Vue项目中利用pdf.js实现pdf内容滑选文字展示与搜索功能
  19. 投资银行理论与实务(二):证券的发行承销与交易
  20. java,内存,存储

热门文章

  1. Ant—使用Ant构建一个简单的Java工程(两)
  2. 搜索引擎 ElasticSearch 之 步步为营2 【基础概念】
  3. 发送不同类型的ActivityFeed
  4. windows7黑屏修复_如何在Windows 10更新后修复黑屏
  5. 如何在Firefox 3中重新启用about:config警告消息
  6. unity 使用tile_如何使用Tile从网上查找电话
  7. 管理员获得所有权_在Windows 7中获得注册表项的所有权
  8. Git vs SVN
  9. SDWebImage 4 0 迁移指南
  10. [从C到C++] 1.3 C++布尔类型(bool)