合集:.NET Core多线程温故知新

去年换工作时系统复习了一下.NET Core多线程相关专题,学习了一线码农老哥的《.NET 5多线程编程实战》课程,我将复习的知识进行了总结形成本专题。

本篇,我们来继续复习一下多线程性能问题的相关知识点,预计阅读时间10分钟。

首先,我们可以明确一下,多线程场景下的常见问题一般为:高CPU占用

一、CPU暴高问题

基本认知

CPU暴高大部分情况下都是线程打暴的!

暴高案例

(1)错误地使用List导致的CPU暴高

常见于偶发性CPU暴高案例中,比如使用了List.Insert(0, item) 时在大数据量下(比如20w+)时间复杂度很大 + 扩容机制,性能很差!一般可能是由模糊查询导致的查了大量DB数据出来组装,因此只会在大数据量时才会偶发。

(2)错误地使用String的拼接导致的CPU暴高

大量错误的大字符串(>85K的都会进LOH)拼接导致LOH频繁触发GC导致CPU暴高。建议使用StringBuilder来重构,但要设置一个合适的初始容量Capacity从而避免频繁对象申请和内存复制。

(3)非线程安全的Dictionary导致的CPU暴高

在多线程环境下使用非线程安全的Dictionary.Contains(key)时导致了在内部实现方法FindEntry(key)时出现了死循环(Entry结构体的next指针指向了自己,由于其他线程也正在Insert、Remove、Update等操作),然后多线程环境下可能有多个死循环一起把CPU打暴了!建议使用线程安全的ConcurrentDictionary结构

(4)lock convoy(锁护送)导致的CPU暴高

在多线程环境下频繁的上下文切换导致,比如每个线程被分配了30ms时间片,但只执行了5ms就被卡主了,即每个请求都有一个lock锁。之前Edison所在的Y公司项目中的JSON-RPC的PreRequest就是这种情况。建议使用批量操作,降低串行化的 lock 个数,不要去玩锁内卷。

(5)应用服务器错误地配置32位导致的CPU暴高

多线程环境下某个方法读取了大量数据(50w+)导致了内存不够用进而引发GC频繁回收进而导致CPU暴高。这常常发生部署在IIS上的.NET Framework Web应用程序:

  • 32bit最高只能吃4G内存;
  • 32bit的临时代(Gen0+Gen1)大概只有不到100M的内存空间;
  • 在IIS服务器模式下,GC会临时征用托管线程充当GC回收线程。

快速解决:将IIS的应用程序域 配置中的 “启用32bit应用程序” 改成False

二、一些实际案例

案例背景

在Edison的前任Y公司,我们做了一些性能优化的措施,提高了系统的稳定性。这里假设之前的系统(大单体)域名为 cj.wzy.cn,每天平均UV(独立用户数) 10000~15000个,平均每天PV(页面浏览数)大概20000~25000个,实时用户数UV(高峰期)800~1000个。虽然这个数值并不高,但是对于这个已经运行了7年多的大单体老系统(.NET 4.5的大Shi山)而言,已经是线上很不稳定了,经常可以看到客服发来的客户抱怨的ticket。

优化内容

(1)优化了一堆年久失修的基础组件

未优化之前存在的问题:

  • jsonrpc的全局PreRequest方法中存在大量 lock convoy (锁护送) 导致线程频繁的上下文切换

    • 比如:每个线程被分配了30ms时间片,但只执行了5ms就被卡主了,即每个请求都有一个lock锁

  • 封装的LocalMemoryCache类基于ReaderWriterLocakSlim对本身就是线程安全的MemoryCache类做线程安全控制

    • 用户态自旋 => 用户态 转 内核态 => 造成CPU压力升高

  • 团队以前自己封装的一个 KafkaHelper 的 Send 方法中加锁范围过大导致等待时间较长

  • ......

(2)优化了一堆慢SQL

未优化之前存在的问题:随着数据量的不断增加,老业务的SQL脚本包含了很多聚合函数、临时表操作 以及 未命中索引的查询条件,解决办法就是SQL优化,对比执行计划 + DBA Review后上线。

(3)优化了IIS的基本配置

未优化之前的问题:部分应用服务器特别是自建的文件服务,经常发生由于配置了“启用32位应用程序”导致的内存不够(因为32位应用最大可用4G内存)用进而引发GC频繁回收进而导致CPU暴高。

解决办法就是将启用32位应用设为False,然后参考一些IIS配置的最佳实践去做了一遍。

当然,根本解法还是去分析自建文件服务中耗内存的地方去优化代码。不过由于当时的物理服务器都是128G的内存且业务场景中也确实存在上传大文件的需求,因此耗内存的地方也暂时搁置去解决了。

(4)优化了滥用Parallel并行库的接口

未优化之前存在的问题:部分耗时较长的Job不加限制的使用 Parallel.ForEach 等方法造成所有CPU Core都被占用并持续数秒,造成CPU>=90%优化后增加了统一的设置的MaxParallelOptions,修复所有滥用的地方传递进去,默认只会用到CPU内核数量的一半。

(5)新增了一台DB服务器分摊压力

有一次因为XXXXXReadDB少了一台,本来是1台写库,2台读库,突然少了一台,导致XXXXXReadDB CPU暴高,应用程序段的DB连接超时严重进而造成延时较多,请求对接,应用程序频繁挂掉。因此后续DBA新增了一台读库,组成1主3从的配置,应用程序段通过切分 合同查询 的业务查询 到 XXXXXReadDB04,所有Job的查询都走XXXXXReadDB03,将流量分摊到不同的读库,保证核心用户的查询流量的可用性。

(6)新增了两台应用服务器分流压力

2021年开始研发中心内部各团队应用开始疯狂调用该系统接口,每分钟请求量达到了1500+左右,造成了原本只是对外部客户服务的应用服务器压力增大,因此新增了两台应用服务器将所有其他团队的内部应用的service请求流量切分到独立的三台服务器上,内外部客户的流量分开,优先保证外部客户的可用性。

未完成的事情

这一切的根因都是因为这七年来这个系统所在的团队单纯拼命的干业务迭代,往原本设计就不佳的大单体系统中堆了太多的屎山,造成了太多的技术债并未及时地去偿还。我们也原本想极力推荐将其拆分后升级到.NET Core或最新的.NET技术,基于.NET Core + 容器化技术 + 开源项目去做较低成本的升级改造,可是计划赶不上变化,当公司从阿什么味的公司找来一高P来做技术总监之后,研发中心所有的Team Leader基本都换成了阿什么味背景的或者靠近阿什么味领导的。此后,所有的计划都是围绕着Java从0到1花费大量成本重构整个大系统来进行,用他们的话来说就是降本增效只能靠Java而不是.NET。公司里整个Java圈子的高级开发者对.NET的认识也还是停留在10年前,我们的发声已变得微不足道,政治正确才是明哲保身的唯一出路。

在这里,Edison还是祝愿Y公司能够越走越好。

参考资料

一线码农,腾讯课堂《.NET 5多线程编程实战

不明作者,《Task调度与await》

作者:周旭龙

出处:https://edisonchou.cnblogs.com

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接。

.NET Core多线 (5) 常见性能问题的更多相关文章

  1. 常见性能优化策略的总结 good

    阅读目录 代码 数据库 缓存 异步 NoSQL JVM调优 多线程与分布式 度量系统(监控.报警.服务依赖管理) 案例一:商家与控制区关系的刷新job 案例二:POI缓存设计与实现 案例三:业务运营后 ...

  2. ASP.NET Core之跨平台的实时性能监控(2.健康检查)

    前言 上篇我们讲了如何使用App Metrics 做一个简单的APM监控,最后提到过健康检查这个东西. 这篇主要就是讲解健康检查的内容. 没看过上篇的,请移步:ASP.NET Core之跨平台的实时性 ...

  3. 健康检查NET Core之跨平台的实时性能监控

    ASP.NET Core之跨平台的实时性能监控(2.健康检查)   前言 上篇我们讲了如何使用App Metrics 做一个简单的APM监控,最后提到过健康检查这个东西. 这篇主要就是讲解健康检查的内 ...

  4. 使用Django.core.cache操作Memcached导致性能不稳定的分析过程

    使用Django.core.cache操作Memcached导致性能不稳定的分析过程 最近测试一项目,用到了Nginx缓存服务,那可真是快啊!2Gb带宽都轻易耗尽. 不过Api接口无法简单使用Ngin ...

  5. Unity游戏项目常见性能问题

    Unity技术支持团队经常会对有需求的客户公司项目进行游戏项目性能审查与优化,在我们碰到过的各种项目相关的问题中也有很多比较共同的方面,这里我们罗列了一些常见的问题并进行了归类,开发者朋友们可以参考下 ...

  6. ASP.NET Core 之跨平台的实时性能监控

    前言 前面我们聊了一下一个应用程序 应该监控的8个关键位置. . 嗯..地址如下: 应用程序的8个关键性能指标以及测量方法 最后卖了个小关子,是关于如何监控ASP.NET Core的. 今天我们就来讲 ...

  7. ios 常见性能优化

    1. 用ARC管理内存 2. 在正确的地方使用reuseIdentifier 3. 尽可能使Views透明 4. 避免庞大的XIB 5. 不要block主线程 6. 在Image Views中调整图片 ...

  8. .NET Core 成都线下面基会拉开序幕

    2017年07月29日下午,由 .NET China Foundation 成都小组组织的 .NET Core 成都地区线下技术交流会在成都成华区某茶楼成功举行,这也是成都地区 .NET Core 非 ...

  9. EF Core 使用编译查询提高性能

    今天,我将向您展示这些EF Core中一个很酷的功能,通过使用显式编译的查询,提高查询性能. 不过在介绍具体内容之前,需要说明一点,EF Core已经对表达式的编译使用了缓存:当您的代码需要重用以前执 ...

  10. async/await 的基本实现和 .NET Core 2.1 中相关性能提升

    前言 这篇文章的开头,笔者想多说两句,不过也是为了以后再也不多嘴这样的话. 在日常工作中,笔者接触得最多的开发工作仍然是在 .NET Core 平台上,当然因为团队领导的开放性和团队风格的多样性(这和 ...

随机推荐

  1. 2020-10-14:Redisson分布式锁超时自动释放,会有什么问题?

    福哥答案2020-10-14:#福大大架构师每日一题# [知乎:](https://www.zhihu.com/question/425541402) 如果线程1的锁被自动释放了,临界区的逻辑还没执行 ...

  2. 2021-12-15: 路径总和 III。给定一个二叉树的根节点 root ,和一个整数 targetSum ,求该二叉树里节点值之和等于 targetSum 的 路径 的数目。路径 不需要从根节点开

    2021-12-15: 路径总和 III.给定一个二叉树的根节点 root ,和一个整数 targetSum ,求该二叉树里节点值之和等于 targetSum 的 路径 的数目.路径 不需要从根节点开 ...

  3. 2021-08-12:约瑟夫环问题。给定一个链表头节点head,和一个正数m,从头开始,每次数到m就杀死当前节点 ,然后被杀节点的下一个节点从1开始重新数, 周而复始直到只剩一个节点,返回最后的节点。

    2021-08-12:约瑟夫环问题.给定一个链表头节点head,和一个正数m,从头开始,每次数到m就杀死当前节点 ,然后被杀节点的下一个节点从1开始重新数, 周而复始直到只剩一个节点,返回最后的节点. ...

  4. flutter 填坑之旅(dart学习笔记篇)

    俗话说 '工欲善其事必先利其器' 想要撸flutter app 而不懂 dart 那就像一个不会英语的人在和英国人交流,懵! 安装 dart 就不用说了,比较简单dart 官网 https://dar ...

  5. Microsoft Office 2019 官方镜像下载 仅支持Win10系统

    Office 2019 专业增强版:(注:这是一个镜像文件) http://officecdn.microsoft.com/pr/492350f6-3a01-4f97-b9c0-c7c6ddf67d6 ...

  6. C++面试八股文:在C++中,你知道哪些运算符?

    某日二师兄参加XXX科技公司的C++工程师开发岗位第11面: 面试官:在C++中,你都知道都哪些运算符? 二师兄:啥?运算符?+-*/=这些算吗? 面试官:嗯,还有其他的吗? 二师兄:当然还有,+=, ...

  7. 理解ASP.NET Core - 全球化&本地化&多语言(Globalization and Localization)

    注:本文隶属于<理解ASP.NET Core>系列文章,请查看置顶博客或点击此处查看全文目录 概述 在众多知名品牌的网站中,比如微软官网.YouTube等,我们经常可以见到"切换 ...

  8. 闺蜜机 StanbyME 产品随想

    今天媳妇告诉我,现在小度这边推出一款叫 "闺蜜机"的可用移动的IPAD设备,我点开链接一看,就感觉兴趣不大,不就是一款把屏幕做的更大些的IPAD了吗? 有哪些更多创新呢?为什么会需 ...

  9. 用XmlSerializer.Deserialize将XML转实体遇到的问题

    1.命名空间的问题 1.1 XML示例: 1.2 反序列化代码: 点击查看源代码 ``` public static object DeserializeFromXml<T>(string ...

  10. LaTeX 的学习笔记

    摘自我的洛谷博客 该文章被打开的次数(包括洛谷平台): \(\LaTeX\) 中所有命令都以\开头,后面可以跟一个花括号,代表参数. \documentclass{} 指定了文章类型,有 articl ...