一:背景

1. 讲故事

现如今的很多代码都是awaitasync+Task的方式,对它们进行性能洞察非常有必要,awaitasync 本质上就是将状态机塞入到 Task 的 m_continuationObject 延续字段上,和 ContinueWith 没有本质区别,这一篇我们就来聊一聊。

二:异步和Task

1. 诊断异步代码时间

这里我就用异步读取 1G文件内容 来举例,参考代码如下:


class Program
{
static async Task Main()
{
// 创建并启动Stopwatch
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start(); string filePath = @"D:\1GB_LogFile.log";
await DoRequest(filePath); // 停止并显示总耗时
stopwatch.Stop();
Console.WriteLine($"总耗时: {stopwatch.Elapsed.TotalSeconds:F2}秒");
Console.ReadLine();
} static async Task DoRequest(string filePath)
{
CheckParameter(); const int chunkSize = 512 * 1024 * 1024; // 每次读取512MB try
{
Console.WriteLine("开始分块读取文件...");
int chunkCount = 0;
long totalBytesRead = 0; using (var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read, 4096, FileOptions.Asynchronous))
{
byte[] buffer = new byte[chunkSize];
int bytesRead; while ((bytesRead = await fileStream.ReadAsync(buffer, 0, buffer.Length)) > 0)
{
totalBytesRead += bytesRead;
chunkCount++; // 处理当前块的数据
string chunkContent = Encoding.UTF8.GetString(buffer, 0, bytesRead);
Console.WriteLine($"读取块 {chunkCount}, 大小: {bytesRead / 1024}KB, 总计: {totalBytesRead / 1024 / 1024}MB");
}
} Console.WriteLine($"文件读取完成,共 {chunkCount} 块"); await Task.CompletedTask;
}
catch (Exception ex)
{
Console.WriteLine($"出错: {ex.Message}");
}
} static void CheckParameter()
{
Console.WriteLine("检查参数开始...");
Thread.Sleep(5000);
Console.WriteLine("检查参数结束...");
}
}

使用 dotrace 的 timeline 模式对程序进行跟踪,可以看到异步方法耗时 6.25s,截图如下:

接下来的问题是这 6.25s 是怎么消耗掉的呢?可以用 F5 搜索 DoRequest 方法的耗时,截图如下:

从图中可以清楚的看到如下信息:

  • CheckParameter: 耗时 5000ms
  • continuations: 即 Task.m_continuationObject 字段中的任务,这是委托到其他线程的执行时间。
  • other: 有 Stream.ReadAsync,JIT 动态编译等等,其实就是底层状态机的部分代码块,参考如下:

如何想观察这 967ms 是如何消耗掉的,可以展开一下,这里要注意一点,这里的深灰色展示的,截图如下:

是不是挺有意思的。

2. 诊断Task代码时间

除了异步代码会横跨多个线程,其实 Task 也有同样的场景,接下来将刚才的异步代码改成 Task模式,核心代码如下:


static Task DoRequest(string filePath)
{
CheckParameter(); const int chunkSize = 512 * 1024 * 1024; // 每次读取512MB return Task.Run(() =>
{
try
{
Console.WriteLine("开始分块读取文件...");
int chunkCount = 0;
long totalBytesRead = 0; using (var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read, 4096, FileOptions.Asynchronous))
{
byte[] buffer = new byte[chunkSize];
int bytesRead; while ((bytesRead = fileStream.Read(buffer, 0, buffer.Length)) > 0)
{
totalBytesRead += bytesRead;
chunkCount++; // 处理当前块的数据
string chunkContent = Encoding.UTF8.GetString(buffer, 0, bytesRead);
Console.WriteLine($"读取块 {chunkCount}, 大小: {bytesRead / 1024}KB, 总计: {totalBytesRead / 1024 / 1024}MB");
}
} Console.WriteLine($"文件读取完成,共 {chunkCount} 块");
}
catch (Exception ex)
{
Console.WriteLine($"出错: {ex.Message}");
}
});
}

使用 dottrace 的 timeline 模式跟踪,拿到跟踪文件之后,使用 F5 搜索 DoRequest 方法,截图如下:

从卦中可以看到 DoRequest 方法消耗了 5010ms,根据调用栈发现没有统计到 Task scheduled -> Program+<>c__DisplayClass1_0.<DoRequest>b__0() 的耗时,这个就有点无语了,不像异步代码有 +Continuation 复选框。。。可以归到Total Time 上,这一点就比较遗憾了。

再说一个比较好的地方,dottrace 专门提供了一个 Task 复选框,它可以观测到追踪时间内程序生成了多少个Task,以及 Task 的执行时间,截图如下:

从卦中的 时间轴 来看,尼玛,Task 怎么跑到 Garbage Collection 线程上执行,这线程是专门用来执行后台GC的,很明显这是有问题的。。。所以也不要太相信 dotTrace 哈。

三:总结

对 异步 和 Task 的下钻分析,非常有利于解决类似线程饥饿Task阻塞等问题,希望本篇能给大家带来一点帮助。

作为JetBrains社区内容合作者,如有购买jetbrains的产品,可以用我的折扣码 HUANGXINCHENG,有25%的内部优惠哦。

DotTrace系列:8. 时间诊断之 异步代码 和 Task任务的更多相关文章

  1. 前端测试框架Jest系列教程 -- Asynchronous(测试异步代码)

    写在前面: 在JavaScript代码中,异步运行是很常见的.当你有异步运行的代码时,Jest需要知道它测试的代码何时完成,然后才能继续进行另一个测试.Jest提供了几种方法来处理这个问题. 测试异步 ...

  2. ANTS Performance Profiler 8:支持对Web请求、异步代码和WinRT的性能剖析

    下载与激活:http://download.csdn.net/detail/lone112/6734291 离线激活   位于英国的Red Gate Software有限公司最近发布了ANTS Per ...

  3. 异步编程系列第01章 Async异步编程简介

    p { display: block; margin: 3px 0 0 0; } --> 2016.10.11补充 三个月过去了,回头来看,我不得不承认这是一系列失败的翻译.过段时间,我将重新翻 ...

  4. Web程序员开发App系列 - 调试Android和IOS手机代码(补图)

    Web程序员开发App系列 Web程序员开发App系列 - 认识HBuilder Web程序员开发App系列 - 申请苹果开发者账号 Web程序员开发App系列 - 调试Android和iOS手机代码 ...

  5. node.js的作用、回调、同步异步代码、事件循环

    http://www.nodeclass.com/articles/39274 一.node.js的作用 I/O的意义,(I/O是输入/输出的简写,如:键盘敲入文本,输入,屏幕上看到文本显示输出.鼠标 ...

  6. 单元测试系列之十:Sonar 常用代码规则整理(二)

    摘要:帮助公司部署了一套sonar平台,经过一段时间运行,发现有一些问题出现频率很高,因此有必要将这些问题进行整理总结和分析,避免再次出现类似问题. 作者原创技术文章,转载请注明出处 ======== ...

  7. 单元测试系列之九:Sonar 常用代码规则整理(一)

    更多原创测试技术文章同步更新到微信公众号 :三国测,敬请扫码关注个人的微信号,感谢! 摘要:公司部署了一套sonar,经过一段时间运行,发现有一些问题出现频率很高,因此有必要将这些问题进行整理总结和分 ...

  8. js 异步代码

    这段时间一直在用node.js做毕设的后台,所以需要一些异步代码操作,主要的异步方式有:Promise.Generator 和 async / await,但下面主要讲 Promise 和 async ...

  9. ASIHTTPRequest系列(一):同步和异步请求

    ASIHTTPRequest系列(一):同步和异步请求 发表于8个月前(2013-11-27 19:21)   阅读(431) | 评论(0) 6人收藏此文章, 我要收藏 赞0 ASIHTTPRequ ...

  10. .NET Core学习笔记(4)——谨慎混合同步和异步代码

    原则上我们应该避免编写混合同步和异步的代码,这其中最大的问题就是很容易出现死锁.让我们来看下面的例子: private void ButtonDelayBlock_Click(object sende ...

随机推荐

  1. 【大数据】HBase 集群部署:全流程详细步骤解析

    [大数据]HBase 集群部署:全流程详细步骤解析 前言 本文帮助你从零搭建一个三台机器(虚拟机)的HBase集群,适用于大数据专业或者用到HBase的同学查看,由于操作步骤过多,特此记录,完整操作过 ...

  2. 如何优化和提高MaxKB回答的质量和准确性?

    目前 ChatGPT.GLM等生成式人工智能在文本生成.文本到图像生成等在各行各业的都有着广泛的应用,但是由于大模型训练集基本都是构建于网络公开的数据,对于一些实时性的.非公开的或离线的数据是无法获取 ...

  3. 洛谷P4198 楼房重建 题解

    Part1.自己一开始是怎么想的 我一开始的想法是先考虑什么情况下是看不见的. 如果是 \(i < j\) 的话可以直接看 \(j\) 的斜率和 \(i\) 的斜率就是比较 \(\frac{h_ ...

  4. Audio DSP boot 过程

    在智能手机或智能手表等SoC上通常有一块专门的audio DSP(简称ADSP)来做音频处理.要做音频处理,ADSP首先要被boot起来.本文以CEVA BX2为例来讲讲ADSP的boot过程. 在上 ...

  5. hashtable底层

    一.单线程环境下 底层:hash表结构 (数组 + 链表) 使用无参构造创建对象时 会默认长度11的数组 加载因子0.75 Hashtable<Object, Object> hashta ...

  6. 适合Java程序员的Go入门笔记

    0.背景 3年java开发背景(因此这篇文章的特点是:比较适合java程序员doge),业余时间有了解过一些go,如今加入字节团队主要技术栈是go,此篇主要结合go语言圣经和团队内go项目,总结一些基 ...

  7. Laravel RCE(CVE-2021-3129)漏洞复现

    Laravel框架简介 Laravel是一套简洁.优雅的PHP Web开发框架(PHP Web Framework).它可以让你从面条一样杂乱的代码中解脱出来:它可以帮你构建一个完美的网络APP,而且 ...

  8. AI Agent现实应用与未来展望:从个人到社会的变革(下篇)

    认知是成本最低的对冲. --张三思维进化论 从理论到实践:Agent技术落地的关键时刻 在前两篇文章中,我们探讨了AI Agent的概念认知和技术原理: 从"被动对话"到" ...

  9. K8s新手系列之Pod中容器的镜像拉取策略

    概述 在 Kubernetes(K8s)里,容器镜像拉取策略(ImagePullPolicy)决定了 K8s 在创建或重启 Pod 时,如何处理容器镜像的拉取操作.这一策略能够确保使用的镜像始终是最新 ...

  10. MySQL同步ES的6种方案!

    引言 在分布式架构中,MySQL与Elasticsearch(ES)的协同已成为解决高并发查询与复杂检索的标配组合. 然而,如何实现两者间的高效数据同步,是架构设计中绕不开的难题. 这篇文章跟大家一起 ...