一码阻塞,万码等待:ASP.NET Core 同步方法调用异步方法“死锁”的真相

在我们 2015 年开始的从 .NET Framework 向 .NET Core 迁移的工程中,遇到的最大的坑就是标题中所说的——同步方法中调用异步方法发生”死锁”。虽然在 .NET Framework 时代就知道不能在同步方法中调用异步方法,但我们却明知路有坑,偏向此路行。不是我们自讨苦吃,而是被迫无奈,因为在 .NET Core 2.0 之前,BCL(基础类库)中有些 API 只有异步实现没有同步实现,比如用于将主机名解析为 IP 地址的 API —— Dns.GetHostAddressesAsync() 。

但最终“被迫无奈”变成“血的教训”,这根本不是坑,而是无底洞。无论在开发与测试环境中多么正常,只要一发布到生产环境有一定并发量就会发生“死锁” —— 大量请求无响应,一直处于等待状态,线程池发飙,线程数持续不断地增长,内存随之增长,直至撑爆服务器(详见当时的一篇随笔 .NET Core 中遇到奇怪的线程死锁问题:内存与线程数不停地增长)。

我们想尽一切方法,用尽网上能找到的同步方法调用异步方法避免死锁的办法,都于事无补,唯有去掉同步方法调用异步方法的代码。当我们意识这是一个无底洞后,赶紧绕道而行,全面放弃在同步方法中调用异步方法,并将“千万千万不要在同步方法中调用异步方法”作为一条 .NET Core 开发准则。

这段踩坑踩到无底洞的血泪史,每当想起都很心痛,心痛不是当时的任何努力都是那么的苍白无力,而是对问题背后原因的困惑 —— 为什么同步方法中 Wait 异步方法会产生如此致命的后果?如果真的千万千万不能这么干,那 .NET Core 为什么不直接在编译时就报错?“死锁”的背后究竟发生了什么?

。。。

2018年10月20日偶然间发现一个网站 —— dotNET Weekly ,在其中发现一篇10月17日发布的博文 —— .NET Threadpool starvation, and how queuing makes it worse,在读懂这篇博文之后,联系到之前踩坑的经历,终于想通了“死锁”的背后(只是个人推测,并不一定正确)。

.NET Core 线程池有 n+1 个队列,每个线程有自己的本地队列(n),整个线程池有一个全局队列(1)。每个线程接活(从队列中取出任务执行)的顺序是这样的:先从自己的本地队列中找活 -> 如果本地队列为空,则从全局队列中找活 -> 如果全局队列为空,则从其他线程的本地队列中抢活。

我们来想象一下异步方法等待同步方法的场景。当10个并发请求到达时(进入的是全局队列),假设线程池中正好有10个空闲线程,这10个线程立马把活接过来,但线程在执行过程中遇到了同步方法等待异步方法(Task.Wait)的情况而进入阻塞状态,无奈地无所事事地在那干等异步方法执行完成而无法帮其他线程干活(这时情况已经有些不妙,由于阻塞线程池少了10个干活的线程)。雪上加霜的是,这些阻塞的线程所等待的异步方法在完成异步操作执行 await 之后的代码时也需要线程,不仅干活的线程少了,而且剩下的线程要干的活更多了(情况更不妙了)。随着并发请求持续不断地进来,形势变得越来越严峻,被阻塞的线程越来越多,能干活的线程越来越少而且要干的活越来越多,于是越来越多的一线干活的线程的队列开始排起了长队。火上浇油的是,那些阻塞着的线程要退出阻塞状态需要等它们所等待的任务被正忙得不可开交的干活线程执行,干活线程越忙,它们被阻塞的时间越长。于是出现了一个奇怪的场面,一群不干活的线程围观并等待着少数干活的线程,眼看着这些干活线程的队列排队越来越长,虽然它们也能干活,但由于它们被关在小黑屋里,无法出手相助,要等它们的主人将它们释放出来,而它们的主人就排在长队中等着从干活线程那拿到小黑屋的钥匙。。。这样的场面最终只有一个结局,所有干活的线程的本地队列都排起了长队,没有空闲的线程。

好戏开始了,不,是灾难开始了。线程池中没有空闲线程,全局队列中的活没人接,于是全局队列开始排队,线程池的线程不够用,如果不赶紧补充线程进来,线程池会被饿死(Threadpool Starvation)。救援行动开始了,CLR 赶紧生产线程喂给线程池,由于全局队列享有最高优先级(根据之前所述的线程接活顺序),一喂进去就被全局队列吃了,但 CLR 一秒钟只能生产1-2个线程,远远满足不了全局队列的胃口,而最需要救援的各个干活线程的本地队列连汤都喝不到。除了 CLR 的外部救援,线程池也同时进行自救,有些线程玩命干活,终于处理完了自己队列中的任务,终于有机会可以帮助其他同伴了,但是它们立即接到了上级命令 —— 以最快速度去救援全局队列,军令不可违,它们眼睁睁地看着同伴绝望地处理着一望无际的长队中的任务,奔赴全局队列,自救也救不到干活线程的本地队列。

这种完全以全局队列为中心、救地位最高的、不救最需要的救援行动最终带来了毁灭性的结果。那些解救全局队列的线程又因为 Task.Wait 而阻塞而需要更多的线程执行阻塞所等待的任务。救援行动变成了自杀行动,线程池就这样被活活饿死了(Threadpool Starvation)。

这就是我所推测的真相,真相背后的真正罪魁祸首其实是对线程的阻塞,所以千万千万不要阻塞(blocking)线程。

原文地址https://www.cnblogs.com/dudu/p/9860959.html

.NET社区新闻,深度好文,欢迎访问公众号文章汇总 http://www.csharpkit.com

640?wx_fmt=jpeg

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/319221.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

codeforces1456 D. XOR-gun

D. XOR-gun 大佬题解 当n比较大的时候,一定有连续相同的3个数最高位都是1,只需要将这三个数其中两个异或一次即可满足题意只需要操作1次。 当n比较小时可以直接暴力,不难发现异或操作要么是一段区间异或然后与区间端点相邻的比较O(n2)O(n^2…

AT4119-[ARC096C]Everything on It【斯特林数,容斥】

正题 题目链接:https://www.luogu.com.cn/problem/AT4119 题目大意 一个集合S{k∈[1,n]∪N}S\{k\in[1,n]\cup N\}S{k∈[1,n]∪N},它的所有子集作为元素组成的集合中要求满足每一个数字的出现之和不小于222,求方案数对PPP取模。 1≤n≤3000,P∈[108,109…

【交互】【随机】Lost Root(CF1061F)

正题 luogu CF1061F 题目大意 给出n和k,现在有一颗n个点的满k叉树,每次查询可以问一个点是否在另外两个点的路径上,让你在 60n60\times n60n 次询问内得到根节点 解题思路 因为是满k叉数,可以先得到深度dep 每次随机找两个点&…

take

take 题解参考 题目描述 Kanade has n boxes , the i-th box has p[i] probability to have an diamond of d[i] size. At the beginning , Kanade has a diamond of 0 size. She will open the boxes from 1-st to n-th. When she open a box,if there is a diamond in it an…

将传统 ASP.NET 应用迁移到 .NET Core

点击蓝字关注我现在越来越多的人在谈论. NET Core。诚然,.NET Core 是未来, 但是.NET Framework 仍在支持, 因为大量的应用程序无法在短时间内迁移。.NET Core 和 .NET Framework 就像电动汽车和汽油动力汽车。汽油车是成熟的,你可以毫无任何问题驾驶它&…

CF932G-Palindrome Partition【PAM】

正题 题目链接:https://www.luogu.com.cn/problem/CF932G 题目大意 给出一个长度为nnn的字符串,将其分为kkk段(kkk为任意偶数),记为ppp。要求满足对于任意iii都有pipk−i1p_ip_{k-i1}pi​pk−i1​。求方案数。 1≤n≤1061\leq n…

2018 ACM ICPC Asia Regional - Seoul B.Cosmetic Survey

参考大佬代码 题目大意 nnn个顾客,mmm种画妆品,每一个顾客会给mmm种化妆品一个值,这个值代表这个化妆品在他心中的排名,排名越小越喜欢,如果这个值为0说明最不喜欢这一种化妆品(值为0理解为无穷大&#xf…

The table(CF226D)

正题 CF226D 题目大意 给一个n*m的矩阵,每次操作可以使一行或一列全部变号(变-,-变),问如何操作可以使所有行列之和都是正数 解题思路 考虑每次暴力找小于0的一行或一列,然后反转 因为每次反转肯定会使…

[翻译] 初看 ASP.NET Core 3.0 即将到来的变化

原文: A first look at changes coming in ASP.NET Core 3.0在我们努力完成下一个 minor 版本的 ASP.NET Core 的同时,我们也在对下一个 major 版本进行更新,其中包括如何使用框架组合项目、更紧密的 .NET Core 集成以及第三方开源集成,所有这…

数字串

来源:牛客网: 题目描述 一个只含数字的字符串,q次操作,每次操作将第i位数字改为x,每次操作后,统计长度在[l, r]之间且首数字大于尾数字的子串的个数。 输入描述: 第一行一个只含数字的字符串&#xff1b…

【随机】Kuroni and the Punishment(CF1305F)

正题 luogu CF1305F 题目大意 给你n个数,每次操作可以使一个数1或-1,让你用最小的操作数使所有数的gcd>1 解题思路 显然把所有数都修改为偶数可以得到 2|gcd,且步数 ≤n\leq n≤n 对于其它方案,至少有一半的数修改次数小于…

hdu5909-Tree Cutting【FWT】

正题 题目链接:http://acm.hdu.edu.cn/showproblem.php?pid5909 题目大意 给出nnn和mmm(m2km2^km2k)。再给出一个大小为nnn的树,每个点有点权,对于每个i∈[1,m)i\in[1,m)i∈[1,m)求有多少个联通子图的点权异或和为iii 1≤T≤10…

codeforces1455 D. Sequence and Swaps

昨天晚上巨困,就没有打,今天课间的时候就看了一下D题,发现好像可以瞎搞,于是吃完饭就写了一下,调过样例一次就A了qaq。 D. Sequence and Swaps 枚举贪心 由于数据范围n≤500n\leq500n≤500,由此我们可以在…

珂朵莉的数列

来源:牛客网: 题目描述 珂朵莉给了你一个序列,有n(n1)/2 ​ 个子区间,求出她们各自的逆序对个数,然后加起来输出 输入描述: 第一行一个数 n 表示这个序列 a 的长度 之后一行 n 个数,第i个数表示ai 输出…

aspnet core 2.1中使用jwt从原理到精通二

在aspnet core中,自定义jwt管道验证有了上一节的内容作为基础,那这点也是非常容易的,关键点在中间件,只是把上一级在测试类中的自定义验证放到中间件中来即可,不过需要注意:中间件 的位置很重要&#xff0c…

【线段树】Traffic Jams in the Land(CF498D)

正题 luogu CF498D 题目大意 给n个1-6的数,让你进行以下操作: 修改其中一个数从第x个数走到第y个数(x≤yx\leq yx≤y),到达一个点时,如果当前时间能被该数整除,那么时间2,否则1&a…

P4201-[NOI2008]设计路线【结论,树形dp】

正题 题目链接:https://www.luogu.com.cn/problem/P4201 题目大意 给出nnn个点的一棵树开始所有边都是白色,选出若干条没有公共点的路径将上面所有边变为黑色。 要求所有点到111号点的路径上经过的白色边的数量的最大值最小。 求最小值和方案数 解题思路 直接记…

codeforces1453 D. Checkpoints

以为又要掉分了(结果没掉说明太菜了),写完ABC还有45分钟,推式子一直没啥结果,最后10分钟想到D题的一个性质,可惜没时间了~ D. Checkpoints Heltion大佬题解 性质:把形如100…01 \ 0 \ 0 \dots…

CentOS开发ASP.NET Core入门教程

因为之前一直没怎么玩过CentOS,大多数时间都是使用Win10进行开发,然后程序都部署在Window Server2008或者Window Server2012上!因此想尝试下Linux系统。最后经过选型选了比较流行的CentOS系统。正好,今晚要加班,所以在…

Java的学习与java大数运算

之前就学过一点java,但太久没用知识点早就还给书本,之前在实验室搞到一本java的书,今天来重新温习一下 java的语法大部分和c语言是一样的,入门非常快,所以在这里基础语句的用法就省略了 输出: System.out.…