最近在改老项目时,干了一件自以为很有成就感的事,心想 “项目都是同步方法,为啥不用异步方法呢?”,于是有了异步方法,类型下面的代码(当然是举例子说明啊)

//更新某人名下公司名称
public Task<bool> UpdateUser(string id,string companyName)
{
var usrInfo=Db.GetUsrInfo(id); var flag= await Db.UpdateCompanyNameAsync(usrInfo.companyId,companyName); return flag
}

“咋一看,好像没啥问题,不就是根据id更新名称吗?”

可实际在测试的时候,报错了,类型下面的错误

在 System.Web.ThreadContext.AssociateWithCurrentThread(Boolean setImpersonationContext)
在 System.Web.HttpApplication.OnThreadEnterPrivate(Boolean setImpersonationContext)
在 System.Web.LegacyAspNetSynchronizationContext.CallCallbackPossiblyUnderLock(SendOrPostCallback callback, Object state)
在 System.Web.LegacyAspNetSynchronizationContext.CallCallback(SendOrPostCallback callback, Object state)
在 System.Web.LegacyAspNetSynchronizationContext.Post(SendOrPostCallback callback, Object state)
在 System.Threading.Tasks.SynchronizationContextAwaitTaskContinuation.PostAction(Object state)
在 System.Threading.Tasks.AwaitTaskContinuation.RunCallback(ContextCallback callback, Object state, Task& currentTask)
--- 引发异常的上一位置中堆栈跟踪的末尾 ---
在 System.Threading.Tasks.AwaitTaskContinuation.<>c.<ThrowAsyncIfNecessary>b__18_0(Object s)
在 System.Threading.QueueUserWorkItemCallback.WaitCallback_Context(Object state)
在 System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
在 System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
在 System.Threading.QueueUserWorkItemCallback.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem()
在 System.Threading.ThreadPoolWorkQueue.Dispatch()
在 System.Threading._ThreadPoolWaitCallback.PerformWaitCallback()

注意:这个错误,在异步方法里用了同步的方法导致的。

有同学此时可能会有疑问,"这个为啥会报这种错误呢"?

别急,这个就涉及到了 “同步上下文”

同步上下文

异步编程必然是关于线程的使用,线程有一个同步上下文的概念,个人认为线程同步上下文是 async/await 遇到最揪心的问题。在现有项目开发中我们可能想尝试使用 async/await,但老代码都是同步方式,这时如果调用一个声明为 async 的方法,死锁和应用程序崩溃的问题一不小心就可能出现。

注意: 控制台程序和.Net Core程序 将不会遇到这个问题,它们不需要同步上下文。

死锁
private static async Task TestAsync()
{
await Task.Delay();
// 业务代码
} public static void TestOne()
{
var task = TestAsync();
task.Wait();
}

以上代码很完美的实现了死锁。 默认情况下,当 Wait() 未完成的 Task 时,会捕获当前线程上下文,在 Task 完成时使用该上下文恢复方法的执行。 当 async 方法内的 await 执行完成时,它会尝试获取调用者线程所在的上下文执行方法的剩余部分, 但是该上下文已含有一个线程,该线程在等待 async 方法完成。然后它们相互等待对方,然后就没有然后了,死在那里。

针对死锁问题的解决方式是增加 ConfigureAwait(false)

// await Task.Delay(1000);
await Task.Delay().ConfigureAwait(false); // 解决死锁

当 await 等待完成时,它会尝试在线程池上下文中执行 async 方法的剩余部分,因此就不存在死锁。

如果项目中,有同步代码,有有很多的异步代码,执行异步代码时的参数是通过同步代码所获取的,那么项目中很有可能会有上述的异常信息

经过查阅资料,和查看园子里其他大佬们的文章了解到

当调用一个 async 方法。如果使用 await 关键字,当前线程立马被释放回线程池,线程的上下文信息会被保存。如果没有使用 await(async void 的方法,必然没有办法使用 await),调用 async 方法之后,代码会继续往下执行,执行完成后当前线程被释放回线程池,线程的上下文信息不会被保存。当 async 中的异步任务执行完成后,会从线程池中获取一个线程继续执行剩余代码,同时会获取当初调用者所在线程的上下文信息(如果当初调用者所在线程没有释放回线程池,上下文信息可以获取到)。那么问题就来了,如果当初调用者没有使用 await 并且 所在线程释放回线程池了,上下文信息因为没有被保持下来,就获取不到了,这时候会抛出异常 未将对象引用设置到对象的实例,经过测试这个异常信息并不一定每次都会出现,原因和线程的释放有关,调用者所在线程的上下文信息存在就不会抛出异常。

参考

传统asp.net小心 async/await坑的更多相关文章

  1. [译]async/await中阻塞死锁

    这篇博文主要是讲解在async/await中使用阻塞式代码导致死锁的问题,以及如何避免出现这种死锁.内容主要是从作者Stephen Cleary的两篇博文中翻译过来. 原文1:Don'tBlock o ...

  2. async/await使用深入详解

    async和await作为异步模型代码编写的语法糖已经提供了一段时间不过一直没怎么用,由于最近需要在BeetleX webapi中集成对Task方法的支持,所以对async和await有了深入的了解和 ...

  3. ASP.NET 中的 Async/Await 简介

    本文转载自MSDN 作者:Stephen Cleary 原文地址:https://msdn.microsoft.com/en-us/magazine/dn802603.aspx 大多数有关 async ...

  4. ASP.NET 上的 Async/Await 简介

    原文链接 大多数有关 async/await 的在线资源假定您正在开发客户端应用程序,但在服务器上有 async 的位置吗?可以非常肯定地回答“有”.本文是对 ASP.NET 上异步请求的概念性概述, ...

  5. C# 中 async/await 调用传统 Begin/End 异步方法

    最近在改进园子的图片上传程序,希望实现用户上传图片时同时将图片文件保存在三个地方:1)服务器本地硬盘:2)又拍云:3)阿里云OSS.并且在保存时使用异步操作. 对于异步保存到本地硬盘,只需用 Stea ...

  6. [.NET 4.5] ADO.NET / ASP.NET 使用 Async 和 Await 异步 存取数据库

    此为文章备份,原文出处(我的网站)  [.NET 4.5] ADO.NET / ASP.NET 使用 Async 和 Await 异步 存取数据库 http://www.dotblogs.com.tw ...

  7. node传统读取文件和promise,async await,

    先上传统文件加载方式代码,传统方式在处理多层嵌套时代码比较混乱 const fs = require('fs') //引入文件系统 function readFile (cb) { fs.readFi ...

  8. 在vue中使用async/await遇到的坑

    最近无聊在搞一些新的东西,今天就遇到一个async/await的坑: 因为我用的不是vue官方的脚手架,所以遇到这样的问题: await is a reserved word 这样的警告,我猜应该是缺 ...

  9. js循环中使用async/await踩过的坑

    最近写koa的时候遇见需要在循环中使用async/await的情况,当然第一反应就是直接上forEach,然后就直接翻车了... 直接上代码: function handleSql(val) { re ...

随机推荐

  1. 怎么清理Linux系统磁盘空间占用大:/dev/xvda1

    1.首先查看磁盘占用情况 df -h Filesystem      Size  Used Avail Use% Mounted on/dev/xvda1       20G   18G  621M  ...

  2. JavaSE基础知识(5)—面向对象(Object类)

    一.包 java.lang包,属于java中的核心包,系统默认导入的,不用手动导入该包中的类:Object.System.String.Integer等 1.包的好处 ①分类管理java文件,查找和管 ...

  3. docker环境下solr6.0配置(中文分词+拼音)

    前言:这篇文章是基于之前的“linux环境下配置solr5.3详细步骤”(http://www.cnblogs.com/zhangyuan0532/p/4826740.html)进行扩展的.本篇的步骤 ...

  4. Subplot 多合一显示

    1.均匀图中图 matplotlib 是可以组合许多的小图, 放在一张大图里面显示的. 使用到的方法叫作 subplot. 使用import导入matplotlib.pyplot模块, 并简写成plt ...

  5. 分布式一致性的基石---Paxos算法(1)

    分布式一致性的基石---Paxos算法(1) Paxos算法是由微软的工程师Lamport提出,Lamport依靠Paxos算法获得图灵奖: Paxos算法旨在解决相互信任的分布式系统中,多个节点能快 ...

  6. Fair Candy Swap LT888

    Alice and Bob have candy bars of different sizes: A[i] is the size of the i-th bar of candy that Ali ...

  7. 由odoo源不稳定想到的,一个非常简单但是有效的方式解决yum源不稳定的问题,实现无限重试

    如题,最近在安装odoo 10, 但是官方源的速度是在是不敢恭维,断断续续的. yum 有没有能够无限重试的参数,于是用python写了个死循环,让它无限重试下去 脚本如下: #! /usr/bin/ ...

  8. linux常用命令简介

    不管是测试还是开发,平时或多或少都要用到Linux命令,下面就把平时必用的一些命令简单总结哈,快学快用 1.   ls : 列举当前目录下文件.子目录的名字,如图举例: (1)  ls  -l  : ...

  9. 编译安装php5.6

    sudo yum -y install curl-devel  bzip2 bzip2-devel libxml2  libxml2-devel  libjpeg libpng freetype li ...

  10. union: php/laravel command

    #########Laravel###############2018-01-09 16:46:26 # switch to maintenance mode php artisan down # s ...