PerfView专题 (第十四篇): 洞察那些 C# 代码中的短命线程

这篇具有很好参考价值的文章主要介绍了PerfView专题 (第十四篇): 洞察那些 C# 代码中的短命线程。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一:背景

1. 讲故事

这篇文章源自于分析一些疑难dump的思考而产生的灵感,在dump分析中经常要寻找的一个答案就是如何找到死亡线程的生前都做了一些什么?参考如下输出:


0:001> !t
ThreadCount:      22
UnstartedThread:  0
BackgroundThread: 1
PendingThread:    0
DeadThread:       20
Hosted Runtime:   no
                                                                         Lock  
       ID OSID ThreadOBJ    State GC Mode     GC Alloc Context  Domain   Count Apt Exception
   0    1 3a74 00efb368     2a020 Preemptive  02F2AF48:00000000 00ec2fa0 1     MTA 
   5    2 6758 00f07a48     2b220 Preemptive  00000000:00000000 00ec2fa0 0     MTA (Finalizer) 
XXXX    3    0 00f31df0   1039820 Preemptive  00000000:00000000 00ec2fa0 0     Ukn (Threadpool Worker) 
XXXX    4    0 00f34080   1039820 Preemptive  00000000:00000000 00ec2fa0 0     Ukn (Threadpool Worker) 
XXXX    5    0 00f363a8   1039820 Preemptive  00000000:00000000 00ec2fa0 0     Ukn (Threadpool Worker) 
XXXX    6    0 00f372e8   1039820 Preemptive  00000000:00000000 00ec2fa0 0     Ukn (Threadpool Worker) 
XXXX    7    0 00f39f80   1039820 Preemptive  00000000:00000000 00ec2fa0 0     Ukn (Threadpool Worker) 
XXXX    8    0 00f3cbd0   1039820 Preemptive  00000000:00000000 00ec2fa0 0     Ukn (Threadpool Worker) 
XXXX    9    0 00f3d128   1039820 Preemptive  00000000:00000000 00ec2fa0 0     Ukn (Threadpool Worker) 
XXXX   10    0 00f40630   1039820 Preemptive  00000000:00000000 00ec2fa0 0     Ukn (Threadpool Worker) 
XXXX   11    0 00f43310   1039820 Preemptive  00000000:00000000 00ec2fa0 0     Ukn (Threadpool Worker) 
XXXX   12    0 00f42db8   1039820 Preemptive  00000000:00000000 00ec2fa0 0     Ukn (Threadpool Worker) 
XXXX   13    0 00f49180   1039820 Preemptive  00000000:00000000 00ec2fa0 0     Ukn (Threadpool Worker) 
XXXX   14    0 00f4a228   1039820 Preemptive  00000000:00000000 00ec2fa0 0     Ukn (Threadpool Worker) 
XXXX   15    0 00f53a28   1039820 Preemptive  00000000:00000000 00ec2fa0 0     Ukn (Threadpool Worker) 
XXXX   16    0 00f56598   1039820 Preemptive  00000000:00000000 00ec2fa0 0     Ukn (Threadpool Worker) 
XXXX   17    0 00f59180   1039820 Preemptive  00000000:00000000 00ec2fa0 0     Ukn (Threadpool Worker) 
XXXX   18    0 00f59b28   1039820 Preemptive  00000000:00000000 00ec2fa0 0     Ukn (Threadpool Worker) 
XXXX   19    0 00f5e8a0   1039820 Preemptive  00000000:00000000 00ec2fa0 0     Ukn (Threadpool Worker) 
XXXX   20    0 00f5f248   1039820 Preemptive  00000000:00000000 00ec2fa0 0     Ukn (Threadpool Worker) 
XXXX   21    0 00f63fc0   1039820 Preemptive  00000000:00000000 00ec2fa0 0     Ukn (Threadpool Worker) 
XXXX   22    0 00f66b50   1039820 Preemptive  00000000:00000000 00ec2fa0 0     Ukn (Threadpool Worker) 

前面的 XXXX 代表线程已死亡,那谁能告诉我 ID=22 的线程生前执行了什么代码呢?其实去年我写了一篇如何用 WinDbg 去寻找程序中的短命线程。 TTD 专题 (第一篇):C# 那些短命线程都在干什么?

虽然可以用 WinDbg 的 TTD 来解决,但也有很多的限制,诸如:

  • 生产环境不能安装 windbg 或者 安装不上
  • 不能对生产程序进行附加

所以这两点也制约了 TTD 的强大威力,那有没有轻量级以及无侵入的方式洞察呢?最近在看 perfview 的文档,发现完全可以使用内核中Thread 的 ETW相关事件来搞定。

二:Thread 的ETW事件

1. 使用 Thread 的短命线程

如果死亡线程背后没有标记 Threadpool Worker 的话,那就说明是代码自己用 new Thread 创建出来的线程,这种比较简单,观察 Windows Kernel/Thread/Start 或者 Microsoft-Windows-DotNETRunning/Thread/Creating 的ETW事件即可。

接下来写一段简单的案例代码:


    internal class Program
    {
        static void Main(string[] args)
        {
            for (int i = 0; i < 5000; i++)
            {
                Test1();
            }

            Console.ReadLine();
        }
        public static int index = 1;

        public static void Test1()
        {
            new Thread(() => { Test2(); }).Start();
        }

        public static void Test2()
        {
            Thread.Sleep(10);

            var i = 10;
            var j = 20;

            var sum = i + j;

            Console.WriteLine($"i={index++},sum={sum}");
        }
    }

代码非常简单,用 new Thread 创建了一个短命线程,接下来打开 Perfview 使用默认配置,完整的 Command 命令如下:


PerfView.exe  "/DataFile:PerfViewData.etl" /BufferSizeMB:256 /StackCompression /CircularMB:500 /ClrEvents:GC,Binder,Security,AppDomainResourceManagement,Contention,Exception,Threading,JITSymbols,Type,GCHeapSurvivalAndMovement,GCHeapAndTypeNames,Stack,ThreadTransfer,Codesymbols,Compilation /NoGui /NoNGenRundown /Merge:True /Zip:True collect

程序跑完后可以用 WinDbg 的 !t 去看看凌乱现场,可以发现有大量的 XXX 线程。


0:008> !t
ThreadCount:      1386
UnstartedThread:  0
BackgroundThread: 2
PendingThread:    0
DeadThread:       1383
Hosted Runtime:   no
                                                                             Lock  
 DBG   ID     OSID ThreadOBJ    State GC Mode     GC Alloc Context  Domain   Count Apt Exception
   0    1     4114 02CAC9C8     2a020 Preemptive  0559F108:0559FFEC 02c9c488 -00001 MTA 
   6    2     31b4 02CBA5F0     2b220 Preemptive  00000000:00000000 02c9c488 -00001 MTA (Finalizer) 
XXXX    3        0 02CCEC48     39820 Preemptive  00000000:00000000 02c9c488 -00001 Ukn 
XXXX  694        0 116C5B18     39820 Preemptive  00000000:00000000 02c9c488 -00001 Ukn 
XXXX  695        0 116C0578     39820 Preemptive  00000000:00000000 02c9c488 -00001 Ukn 
XXXX  696        0 116C1250     39820 Preemptive  00000000:00000000 02c9c488 -00001 Ukn 
XXXX  697        0 116BF8A0     39820 Preemptive  00000000:00000000 02c9c488 -00001 Ukn 
XXXX  698        0 116C5F60     39820 Preemptive  00000000:00000000 02c9c488 -00001 Ukn 
XXXX  699        0 116C38D8     39820 Preemptive  00000000:00000000 02c9c488 -00001 Ukn 
XXXX  700        0 116C74C8     39820 Preemptive  00000000:00000000 02c9c488 -00001 Ukn 
...
XXXX 1380        0 115097C0     39820 Preemptive  00000000:00000000 02c9c488 -00001 Ukn 
XXXX 1381        0 115079C8     39820 Preemptive  00000000:00000000 02c9c488 -00001 Ukn 
XXXX 1382        0 1150B170     39820 Preemptive  00000000:00000000 02c9c488 -00001 Ukn 
XXXX 1383        0 1150AD28     39820 Preemptive  00000000:00000000 02c9c488 -00001 Ukn 
XXXX 1384        0 11508258     39820 Preemptive  00000000:00000000 02c9c488 -00001 Ukn 
XXXX 1385        0 11505BD0     39820 Preemptive  00000000:00000000 02c9c488 -00001 Ukn 
   7 1386     6114 1150CF68     20220 Preemptive  055A07A8:055A1FEC 02c9c488 -00001 Ukn 

收集一会之后停止收集,选中Thread的内核事件 Thread/Start,截图如下:

PerfView专题 (第十四篇): 洞察那些 C# 代码中的短命线程

从卦中可以看到有大量的 Start 事件,我们挑选其中一个观察下线程栈,右键 Open Any Stacks 看看到底是什么代码发出了这个 ETW 事件,截图如下:

PerfView专题 (第十四篇): 洞察那些 C# 代码中的短命线程

从卦中可以清晰的看到,原来是 Main() -> Test1() 方法创建的哈,终于水落石出。

2. 使用 ThreadPool 的短命线程

在真实场景中也有很多代码是用 ThreadPool 创建出来的短命线程,这种短命线程其实有一个特点,那就是曾经有大量的任务进队列,导致 ThreadPool 被迫生成很多的线程来应付,当任务全部被消灭后,ThreadPool 就会把那些被迫生成的线程全部给裁掉

卸磨杀驴,真的好像我们的职场/(ㄒoㄒ)/~~。

所以突破点就是统计下 ThreadPoolEnqueueWork 事件,有了思路之后修改下测试代码。


        public static void Test1()
        {
            Task.Run(() => { Test2(); });
        }

这里有一个注意点,程序跑完之后还要稍等一两分钟,就是让ThreadPool把多余的Thread给灭掉,用 windbg 观察到的效果图就是 讲故事 那一节的,停止 perfview 收集后,寻找 ThreadPoolEnqueueWork 事件,截图如下:

PerfView专题 (第十四篇): 洞察那些 C# 代码中的短命线程

从卦中可以看到有大量的 ThreadPoolEnqueueWork 事件,接下来可以选择右键菜单 Save View as Excel 导出到 Excel 中,然后对 Time Msec 进行分组排序,看下哪一个时间段有大量的任务进队列,指标高的时间段自然就是重点怀疑的。

这里要说一点 Time MSecTrace Start Time 基础上的毫秒级偏移值。

PerfView专题 (第十四篇): 洞察那些 C# 代码中的短命线程

举个例子: 4377.032 (4.37s) + 15:56:25.566 = 15:56:29.866

有了这些概念之后,找到问题区域的进队任务,观察下调用栈,大概率也能找到问题,从调用栈来看,原来是 Test1() 所致哈。。。 截图如下:

PerfView专题 (第十四篇): 洞察那些 C# 代码中的短命线程

三:总结

相比WinDbg TTD的重模式,Perfiew真的很轻,而且无侵入性,这两个工具真的是珠联璧合,相得益彰。文章来源地址https://www.toymoban.com/news/detail-581072.html

PerfView专题 (第十四篇): 洞察那些 C# 代码中的短命线程

到了这里,关于PerfView专题 (第十四篇): 洞察那些 C# 代码中的短命线程的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • MySQL篇----第十四篇

    前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站,这篇文章男女通用,看懂了就去分享给你的码吧。 1、设计良好的数据库结构,允许部分数据冗余,尽量避免 join 查询,提高效率。 2、选择合适的表字段数

    2024年02月19日
    浏览(68)
  • Java基础---第十四篇

    平时可能大家使用的最多的就是使用 String 作为 HashMap 的 key,但是现在我们想使用某个自定 义类作为 HashMap 的 key,那就需要注意以下几点: 如果类重写了 equals 方法,它也应该重写 hashCode 方法。 类的所有实例需要遵循与 equals 和 hashCode 相关的规则。 如果一个类没有使用

    2024年02月07日
    浏览(46)
  • 【Python入门系列】第十四篇:Python Web开发

    PythonWeb开发是使用Python语言进行Web应用程序开发的过程。Python是一种简洁、易读且功能强大的编程语言,因此在Web开发领域广受欢迎。 PythonWeb开发可以涵盖多个方面,包括服务器端开发、数据库管理、前端设计和API开发等。在服务器端开发中,常用的Python Web框架有Django、F

    2024年02月16日
    浏览(43)
  • 二十三种设计模式第十四篇--策略模式

    策略模式:主要围绕一个类的行为或者其算法在运行时更改,也是一种行为型模式。 在软件开发中,我们经常遇到需要根据不同的情况选择不同算法或行为的情况。传统的做法是使用大量的条件语句来实现这种逻辑,但这样的实现方式往往难以维护和扩展。策略模式(Strat

    2024年02月12日
    浏览(62)
  • 【接口测试基础】第十四篇 | iHRM项目之登录及员工管理实战

    简介: 功能模块: 技术架构: 前端:以Node.js为核心的Vue.js前端技术生态架构 后端:SprintBoot+SprintCloud+SprintMVC+SprintData(Spring全家桶) MySQL+Redis+RabbitMQ 初始化项目环境 1.新建用例集 2.创建环境变量 3.添加断言 4.其它接口共性分析 4.1.由于是同一个接口,因此他们的请求方法、URL、

    2024年02月02日
    浏览(52)
  • 用 PerfView 洞察.NET程序非托管句柄泄露

    1. 讲故事 前几天写了一篇 如何洞察 .NET程序 非托管句柄泄露 的文章,文中使用 WinDbg 的 !htrace 命令实现了句柄泄露的洞察,在文末我也说了,WinDbg 是以侵入式的方式解决了这个问题,在生产环境中大多数情况下是不能走附加进程的模式,所以这也是它最大的局限性。 那如何

    2024年02月16日
    浏览(45)
  • 第十四届蓝桥杯编程题部分代码题解

    C. 冶炼金属 最大值就是取 a / b a / b a / b 的最小值,最小值就是二分找到满足 m i d ∗ ( b i + 1 ) ≥ a i mid * (b_i + 1) ≥ a_i mi d ∗ ( b i ​ + 1 ) ≥ a i ​ 的最小值 D. 飞机降落 全排列枚举所有降落方案,然后判断即可 E. 接龙数列 状态定义: f [ i , j ] f[i, j] f [ i , j ] 为前 i i i 个数,

    2023年04月11日
    浏览(43)
  • 第十四届蓝桥杯校模拟赛-编程大题详解+代码(二)

    前言: 这几天有不少小伙伴催促我尽快更新后五道编程题题解,然鄙人实在水平有限,实事求是,能力不足,不堪众望。思索良久,第九题有解题思路且已完成部分解题,但未完全完成,第十题尚未有思路。在此愿有大佬指点一二! 目录 一、做不完的核酸 问题描述 1.1 代码

    2024年02月02日
    浏览(53)
  • Java多线程&并发篇----第二十四篇

    前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站,这篇文章男女通用,看懂了就去分享给你的码吧。 采用时间片轮转的方式。可以设置线程的优先级,会映射到下层的系统上

    2024年01月20日
    浏览(70)
  • 第十四届蓝桥杯(电子类)单片机开发——赛题源代码分享

          需要源代码一键三联,私聊我哦

    2024年02月05日
    浏览(63)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包