DotTrace系列:8. 时间诊断之 异步代码 和 Task任务
一:背景
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任务的更多相关文章
- 前端测试框架Jest系列教程 -- Asynchronous(测试异步代码)
写在前面: 在JavaScript代码中,异步运行是很常见的.当你有异步运行的代码时,Jest需要知道它测试的代码何时完成,然后才能继续进行另一个测试.Jest提供了几种方法来处理这个问题. 测试异步 ...
- ANTS Performance Profiler 8:支持对Web请求、异步代码和WinRT的性能剖析
下载与激活:http://download.csdn.net/detail/lone112/6734291 离线激活 位于英国的Red Gate Software有限公司最近发布了ANTS Per ...
- 异步编程系列第01章 Async异步编程简介
p { display: block; margin: 3px 0 0 0; } --> 2016.10.11补充 三个月过去了,回头来看,我不得不承认这是一系列失败的翻译.过段时间,我将重新翻 ...
- Web程序员开发App系列 - 调试Android和IOS手机代码(补图)
Web程序员开发App系列 Web程序员开发App系列 - 认识HBuilder Web程序员开发App系列 - 申请苹果开发者账号 Web程序员开发App系列 - 调试Android和iOS手机代码 ...
- node.js的作用、回调、同步异步代码、事件循环
http://www.nodeclass.com/articles/39274 一.node.js的作用 I/O的意义,(I/O是输入/输出的简写,如:键盘敲入文本,输入,屏幕上看到文本显示输出.鼠标 ...
- 单元测试系列之十:Sonar 常用代码规则整理(二)
摘要:帮助公司部署了一套sonar平台,经过一段时间运行,发现有一些问题出现频率很高,因此有必要将这些问题进行整理总结和分析,避免再次出现类似问题. 作者原创技术文章,转载请注明出处 ======== ...
- 单元测试系列之九:Sonar 常用代码规则整理(一)
更多原创测试技术文章同步更新到微信公众号 :三国测,敬请扫码关注个人的微信号,感谢! 摘要:公司部署了一套sonar,经过一段时间运行,发现有一些问题出现频率很高,因此有必要将这些问题进行整理总结和分 ...
- js 异步代码
这段时间一直在用node.js做毕设的后台,所以需要一些异步代码操作,主要的异步方式有:Promise.Generator 和 async / await,但下面主要讲 Promise 和 async ...
- ASIHTTPRequest系列(一):同步和异步请求
ASIHTTPRequest系列(一):同步和异步请求 发表于8个月前(2013-11-27 19:21) 阅读(431) | 评论(0) 6人收藏此文章, 我要收藏 赞0 ASIHTTPRequ ...
- .NET Core学习笔记(4)——谨慎混合同步和异步代码
原则上我们应该避免编写混合同步和异步的代码,这其中最大的问题就是很容易出现死锁.让我们来看下面的例子: private void ButtonDelayBlock_Click(object sende ...
随机推荐
- Arrays工具类--java进阶day06
1.Arrays工具类 这些方法都是针对数组,并且都被static修饰,可以直接使用类名进行调用 1.toString 将数组拼接成带有相对应格式的字符串,可用于展示数组 2.equals 比较两个数 ...
- 内部类--成员内部类、静态内部类、局部内部类--java进阶day03
1.内部类 内部类分为4种,成员内部类用处不大,静态内部类和局部内部类更是鸡肋,唯有匿名内部类是需要我们重点掌握的 1.成员内部类 Inter类要访问Outer类的成员可以直接访问,而Outer要访问 ...
- static修饰成员变量的特点及static修饰成员变量内存图解-java se进阶 day01
1.static介绍 static是静态的意思,它可以用于修饰成员变量和成员方法 2.static的特点 1.被static修饰了的成员变量,可以被类中的所有对象所共享 虽然stu02没有给schoo ...
- study Rust-8【使用结构体的方法】
1.方法 与函数类似:它们使用 fn 关键字和名称声明,可以拥有参数和返回值,同时包含在某处调用该方法时会执行的代码.2.不过方法与函数是不同的,因为它们在结构体的上下文中被定义(或者是枚举或 tra ...
- mybatis报错Cause: java.lang.IllegalArgumentException: invalid comparison: java.util.ArrayList and java
原因 传入参数为List<String>不能用lists != '' 判断 解决 将lists != '' 的判断去掉或者改为lists .size>0 其他 如果是Integer类 ...
- Unity性能优化-降低功耗,发热量,耗电量之OnDemandRendering篇
公司游戏项目,手机运行严重发烫,耗电量飞快.在暂时无法做其他美术性和技术性优化的情况下,我写了这个公司内部文档,并做了个实验,今天干脆公布出来,希望对大家有用. --官方文档: Unity - Scr ...
- 一句话秒建公网站!AI边缘计算颠覆传统开发
一句话就能让 AI 搭建一个公网可访问的完整网站: 短短几秒钟内,AI 便能完成所有构建操作: 这或许是目前全球最简便的建站方案: 本文使用的 AI 工具为腾讯云的 EdgeOne Pages MCP ...
- 🎀dubbo QOS介绍及命令
简介 在Dubbo中,QoS(Quality of Service)功能是一个非常重要的特性,用于提供对运行时服务的查询和控制能力. QoS的概念源自网络设备中的服务质量保障机制,但在Dubbo中,它 ...
- 🎯Vercel-从零到上线的云端部署神器
简介 Vercel是一个专注于前端和全栈应用部署的云端平台,由Zeit公司开发.它以零配置部署.全球CDN加速和对主流框架的深度支持为核心优势,成为开发者快速上线项目的首选工具.无论是个人博客.企业官 ...
- Wireshark 的抓包和分析,看这篇就够了!
原文:Wireshark 的抓包和分析,看这篇就够了!