线程 视图是并发可视化工具中最详细且功能丰富的视图。 在 “线程 ”视图中,可以识别执行段期间正在执行代码的线程,并分析线程是由于同步、I/O 还是其他原因而正在执行或阻止。 线程 视图报告还会分析调用堆栈树的执行情况以及取消阻止线程。
当线程正在执行时,并发可视化工具会收集示例。 线程停止执行后,可视化工具将检查线程的所有作系统上下文切换事件。 可能会发生上下文切换,因为:
- 线程在同步基元上被阻塞。
- 线程的量子过期。
- 线程发出阻塞 I/O 请求。
并发可视化工具对线程和上下文切换事件进行分类,并搜索线程的调用堆栈以获取已知的阻止 API。 它在 “线程” 视图中左下角显示活动图例中的线程类别。 在大多数情况下,可以通过检查对应于上下文切换事件的调用堆栈来确定阻塞事件的根本原因。
如果没有调用堆栈匹配,并发可视化工具使用 Windows 提供的等待原因。 但是,Windows 类别可能基于实现详细信息,并且可能不会反映用户意向。 例如,Windows 报告在 I/O 而不是同步时阻止本机精简读取器写入器锁的等待原因。
“线程 ”视图还显示线程之间的依赖关系。 例如,如果你标识在同步对象上被阻止的线程,则可以找到取消阻止它的线程。 可以检查解除阻塞线程在解除阻塞其他线程时的调用堆栈。
可以使用 “线程 ”视图来:
- 确定应用用户界面(UI)在某些执行阶段无响应的原因。
- 确定在同步、I/O、页面错误和其他事件上阻塞所花费的时间。
- 发现系统上正在执行的其他进程的干扰程度。
- 确定并行执行的负载均衡问题。
- 查找欠佳或不存在可伸缩性的原因。 例如,当有更多逻辑核心可用时,并行应用的性能为何没有提高。
- 了解应用中的并发程度,以帮助并行化。
- 确定工作线程之间的依赖关系和执行的关键路径。
使用“线程”视图
若要启动并发可视化工具,请选择“ 分析>并发可视化工具”,然后选择一个选项,例如 “启动新进程”。
并发可视化启动应用并收集跟踪数据,直到选择 停止收集。 然后,可视化工具分析跟踪并在跟踪报表页上显示结果。
选择报表左上角的“ 线程 ”选项卡以打开 “线程 ”视图。
选择时间间隔和线程以启动性能分析。
时间线分析
“线程”视图的上半部分是时间线。 时间线显示进程中所有线程的活动以及主计算机上的所有物理磁盘设备。 它还显示 GPU 活动和标记事件。
在时间线上,x 轴是时间轴,在 y 轴上是多个通道:
- 系统上每个磁盘驱动器的两个 I/O 通道、一个用于读取的通道和一个用于写入的通道。
- 进程中每个线程的通道。
- 标记通道(如果跟踪中有标记事件)。 标记通道最初显示在生成这些事件的线程通道下。
- GPU 通道。
最初,线程按创建的顺序进行排序,因此主应用线程首先排序。 在“ 排序依据 ”下拉列表中选择另一个选项,以按另一个条件(如 执行)对线程进行排序。
时间线颜色指示给定时间线程的状态。 绿色段正在执行,红色段因同步被阻止,黄色段被抢占,紫色段正在进行设备输入/输出操作。
可以放大以查看更多详细信息,或缩小以查看更长的时间间隔。 选择图形上的段和点,以获取有关类别、开始时间、延迟和调用堆栈状态的详细信息。
使用时间线检查并行循环或并发任务中涉及的线程之间的工作平衡。 如果一个线程比其他线程耗时较长,则任务可能不平衡。 可以通过在线程之间更均匀地分配工作来提高应用的性能。
如果某个时间点只执行一个线程,则应用可能无法充分利用系统上的并发性。 可以使用时间线图来检查线程之间的依赖关系,以及阻塞线程和被阻塞线程之间的时间关系。 若要重新排列线程,请选择一个线程,然后选择工具栏上的向上或向下图标。
可以隐藏未在工作的线程或完全被阻塞的线程,因为它们的统计信息无关紧要,并且可能会使报告变得混乱。 通过选择线程的名称,然后在工具栏上选择 隐藏所选线程 或 隐藏除所选线程外的所有线程 图标来隐藏线程。 若要标识要隐藏的线程,请选择左下角的 “每线程摘要 ”链接。 可以在 “每线程摘要” 图中隐藏没有活动的线程。
线程执行详细信息
若要获取有关执行段的更多详细信息,请选择时间线绿色段上的点。 并发可视化工具在所选点上方显示黑色插入符号,并在底部窗格的“当前”选项卡上显示其调用堆栈。 可以在执行段上选择多个点。
注释
如果段的持续时间小于 1 毫秒,并发可视化工具可能无法解析执行段的选择。
若要获取当前所选时间范围内所有未隐藏线程的执行配置文件,请选择左下角图例中的 “执行 ”。
线程阻止详细信息
若要获取有关线程上特定区域的信息,请将鼠标悬停在日程表上的该区域上以显示工具提示。 工具提示包含类别、开始时间和延迟等信息。 选择区域以在底部窗格的 “当前 ”选项卡中显示该时间点的调用堆栈。 该窗格还显示类别、延迟、阻止 API(如果有)和取消阻止线程(如果有)。 通过检查调用堆栈,可以确定线程阻塞事件的根本原因。
执行路径可能有多个阻塞事件。 若要通过阻止类别检查这些问题并更快地查找问题区域,请选择左侧图例中的阻止类别。
线程之间的依赖关系
并发可视化工具显示线程之间的依赖关系,因此可以确定阻塞线程正在尝试执行的操作,以及使其执行的其他线程。
若要确定哪个线程解除阻塞了另一个线程,请选择时间线上的阻塞段。 如果并发可视化工具可以确定解除阻止的线程,则会在取消阻止线程与紧随阻塞段的执行段之间绘制一条线。 选择底部窗格中的 “取消阻止堆栈 ”选项卡以查看相关的调用堆栈。
概要报告
时间线图下方是一个窗格,其中包含 “配置文件报表”、“ 当前”和“ 取消阻止堆栈 ”报表选项卡。 更改时间线和线程选择时,报表会自动更新。 对于大型跟踪,当计算更新时,报告窗格可能会暂时不可用。
“个人资料报表”选项卡
概要报告有两个筛选器:
- 若要筛选出很少花费时间的调用树条目,请在降噪处字段中键入介于 0 到 99% 之间的过滤值。 默认值为 2%。
- 若要查看仅代码的调用树,请选中“ 仅我的代码 ”复选框。 若要查看所有调用树,请清除复选框。
“概要报表”选项卡显示说明中类别和链接的报表。 若要显示报表,请选择左侧的其中一个条目:
执行“执行”报告显示应用程序在执行中花费的时间明细。
若要查找执行时间所在的代码行,请展开调用树,然后在调用树条目的快捷菜单上选择查看源或查看调用位置。 查看源 查找已执行的代码行。 查看呼叫站点 可找到调用执行行的代码行。 如果只有一个呼叫站点线路存在,则会突出显示其代码。 如果存在多个呼叫站点,请在对话框中选择所需站点,然后选择“ 转到源”。 查找具有最多实例、最多时间或两者兼有的调用站点通常最有用。 有关详细信息,请参阅 “执行配置文件”报表。
同步同步报告显示负责同步块的调用以及每个调用堆栈的总阻塞时间。 有关详细信息,请参阅 同步时间。
I/OI/O 报告显示负责 I/O 块的调用,以及每个调用堆栈的总阻塞时间。 有关详细信息,请参阅 I/O 时间(线程视图)。
SleepSleep报告显示那些导致睡眠阻塞的函数调用,以及每个调用堆栈的总体阻塞时间。 有关详细信息,请参阅 睡眠时间。
内存管理内存管理报告显示发生内存管理块的调用,以及每个调用堆栈的总阻塞时间。 使用此信息可识别出现过多分页或垃圾回收问题的区域。 有关详细信息,请参阅 内存管理时间。
抢占抢占报告显示系统上的进程在哪里抢占了当前进程,以及哪些线程在当前进程中被其他线程替换。 可以使用此信息来识别主要导致抢占的进程和线程。 有关详细信息,请参阅 抢占时间。
UI 处理UI 处理报表显示负责 UI 处理块的调用,以及每个调用堆栈的总阻塞时间。 有关详细信息,请参阅 UI 处理时间。
每个线程摘要 选择 “每线程摘要 ”以显示显示当前所选时间间隔的线程状态的图形。 颜色编码列显示每个线程在运行、阻塞、I/O 和其他状态中花费的总时间。 底部为线程标记。 调整时间线图中的缩放级别时,此图会自动更新。
在某些缩放级别,某些线程可能不会显示在图形中。 发生这种情况时,省略号(...)显示在右侧。 如果所需的线程未显示,可以隐藏其他线程。 有关详细信息,请参阅 “每线程摘要”报表。
磁盘作 选择 “磁盘作 ”以显示当前进程的磁盘 I/O 中涉及的进程和线程、他们触摸的文件(例如加载的 DLL)、读取的字节数和其他信息。 可以使用此报告来评估在执行期间访问文件所用的时间,尤其是在进程似乎受 I/O 约束时。 更多信息,请参阅 磁盘操作报告。
“当前”选项卡
此选项卡显示时间线图中线程段上选定点的调用堆栈。 调用堆栈经过剪裁,仅显示与应用相关的活动。
“解锁堆栈”选项卡
此选项卡显示哪个线程解除阻止了所选线程,以及解除阻止调用堆栈。
通道(线程视图)
并发可视化工具显示四种类型的通道:线程通道、磁盘通道、标记通道和 GPU 通道。
线程通道
线程通道仅显示一个线程的线程状态(按颜色)。 在通道名称上暂停时,将显示给定线程的启动函数。 并发可视化工具可检测多种线程。 下表显示了最常见的类型。
| Thread | Description |
|---|---|
| 主线程 | 启动应用的线程。 |
| 工作线程 | 由应用程序主线程创建的线程。 |
| CLR 工作线程 | 由公共语言运行时(CLR)创建的工作线程。 |
| 调试器助手 | 由 Visual Studio 调试器创建的工作线程。 |
| ConcRT 线程 | 由Microsoft并发运行时创建的线程。 |
| GDI 线程 | 由 GDIPlus 创建的线程。 |
| OLE/RPC 线程 | 作为 RPC 工作线程创建的线程。 |
| RPC 线程 | 作为 RPC 线程创建的线程。 |
| Winsock 线程 | 作为 Winsock 线程创建的线程。 |
| 线程池 | 由 CLR 的线程池创建的线程。 |
磁盘通道
磁盘通道对应于计算机中的物理驱动器。 由于系统上每个物理驱动器都有单独的读写作通道,因此每个驱动器都有两个通道。 磁盘编号对应于内核设备名称。 仅当磁盘上存在活动时,才会显示磁盘通道。
标记通道
标记通道对应于应用生成的事件及其使用的库。 例如,任务并行库、并行模式库和C++ AMP 生成显示为标记的事件。 每个标记通道都与线程 ID 相关联,该 ID 显示在通道的说明旁边。 ID 标识生成事件的线程。 通道的说明包括生成事件的 Windows 事件跟踪 (ETW) 提供程序的名称。 如果 并发可视化工具 SDK 的事件在通道中显示,系列名称也会显示。
GPU 通道
GPU 通道显示系统上有关 DirectX 11 活动的信息。 与图形卡关联的每个 DirectX 引擎都有单独的通道。 各个段表示处理 DMA 数据包所用的时间。
复制选择
若要从报表选项卡中复制整个调用堆栈,请单击“ 复制”。 然后,可以将调用堆栈粘贴到支持该作的任何程序中。
“当前”选项卡
通过单击 “当前 ”选项卡,可以看到一个调用堆栈(如果可用),如果选中了 CPU 线程段,则它最接近时间线中的当前选择点。 在这种情况下,选择点由时间线上方的黑色箭头或插入符号表示。 选择阻塞段时,不会显示插入符号,因为没有执行。 但是,该代码段仍突出显示,并显示调用堆栈。
“ 当前 ”选项卡还显示有关 DirectX 活动段、标记和 I/O 访问的信息。 对于 DirectX 活动段,显示有关硬件队列处理 DMA 包方式的信息。 对于标记,将显示有关说明和标记类型的信息。 对于 I/O 访问,将显示有关文件的信息以及读取或写入的字节数。
空时间轴段
在并发可视化工具中,时间线部分为空(背景为白色)的原因取决于通道类型。
对于 CPU 线程通道,这意味着该线程在时间线的这一部分不存在。 如果对线程感兴趣,可以使用缩放控件或水平滚动找到其执行部分。
对于 I/O 通道,这意味着该时间点没有代表目标进程进行磁盘访问。
对于 DirectX 通道,这意味着在时间线的这一部分,没有为目标进程执行任何 GPU 工作。
对于标记通道,这意味着未生成任何标记。
“导出”按钮(并发可视化工具)
使用 导出 按钮可以将调用堆栈导出为 csv 文件,以便记录或用于其他工具,例如 Microsoft Excel。
仅我的代码 (线程视图)
如果选择此选项,将筛选调用堆栈,以便仅显示您的代码和被调用的函数一个级别。
通过激活此选项,可以显著减少调用堆栈的复杂性,并可能更轻松地诊断特定问题。
在某些情况下,选择此选项可能会筛选掉阻止调用。 如果需要完整调用堆栈详细信息才能确定该决定,请清除此选项以公开完整调用堆栈。
管理频道
在并发可视化工具的 “线程视图 ”中,可以组织进程的通道,以便检查特定模式。 你可以对频道进行排序、上下移动,以及隐藏或显示频道。
排序依据
可以使用排序依据控件根据当前缩放级别按不同条件对线程进行排序。 在查找特定模式时,这特别有用。 可以按以下条件进行排序:
| 条件 | Definition |
|---|---|
| 开始时间 | 按线程的开始时间对线程进行排序。 这是默认排序顺序。 |
| 结束时间 | 按线程的结束时间对线程进行排序。 |
| Execution | 按执行中花费的时间百分比对线程进行排序。 |
| Synchronization | 按同步所用的时间百分比对线程进行排序。 |
| I/O | 按 I/O 中花费的时间百分比(读取和写入数据)对线程进行排序。 |
| 睡眠 | 按睡眠时间的百分比对线程进行排序。 |
| Paging | 按分页所用的时间百分比对线程进行排序。 |
| 抢占 | 按抢占所用的时间百分比对线程进行排序。 |
| UI 处理 | 按用户界面处理中花费的时间百分比对线程进行排序。 |
向上或向下移动所选通道
可以使用这些控件在列表中向上或向下移动通道。 例如,可以将相关通道彼此放置,以帮助检查特定模式或跨线程关系。
将所选通道移动到顶部或底部
可以将所选通道移动到列表的顶部或底部,以便检查特定模式,或者在检查其他通道时移出某些通道。
隐藏所选通道
如果要隐藏通道,请选择此控件。 例如,如果某个线程在其托管进程的整个生命周期中都是100%同步的,那么在分析其他线程时,您可以将其隐藏。
注释
隐藏线程还会从计算时间中删除该线程,该计算时间显示在活动图例和配置文件报表中。
显示所有频道
当隐藏一个或多个通道时,此控件处于活动状态。 如果选中这个选项,将会显示所有隐藏元素,并重新纳入时间计算。
将标记移动到顶部
如果跟踪包含标记事件,则可以使用此命令将标记通道移动到时间线的顶部。 保留其相对顺序。
按线程对标记进行分组
如果跟踪包含标记事件,则可以使用此命令对生成标记事件的线程下的标记通道进行分组。 磁盘通道将移动到通道列表的顶部,GPU 通道将移动到底部。
打开/关闭度量模式
通过使用此工具,可以精确测量时间线中的时间长度。 若要启用度量模式,请单击度量值按钮(其中包含标尺图标),然后在时间线中拖动。 拖动时,请注意指针下方的区域突出显示为黄色,测量的时间显示在按钮右侧的工具栏中。 在拖动时动态计算此值,以便可以立即查看特定事件占用的时间。 释放鼠标按钮时,时间值将保持可见。
可以重复度量过程,但只显示最新的度量值。 再次单击度量值按钮以关闭度量值模式。
降噪百分比
默认情况下,降噪百分比设置的值为 2。 调用树中仅显示包容时间百分比大于或等于此设置的条目。 通过更改设置,可以控制调用树中显示的条目数。 例如,将值更改为 10 将仅显示包含时间大于或等于 10%的调用树条目。 通过增加设置的值,可以专注于对进程性能产生较大影响的条目。
基于可见时间范围的报表
“概要视图”显示基于当前可见的时间范围和通道的报表。 若要查看数据的不同子集的详细信息,请单击图例中的项。
可以在 “线程视图”报表中找到有关数据的详细信息。
线程就绪连接器
单击阻塞段以查看调用堆栈及其解除阻塞堆栈时,也可能会显示线程就绪连接器。 如果取消阻止事件发生在当前进程中的另一个线程上,则线程就绪连接器直观地标识线程和执行段,使阻塞线程能够恢复执行。
时间线光标
在时间轴上选择正在执行的线程段中的一个点时,时间轴插入符将显示在该点的上方。 当前堆栈选项卡上显示的调用堆栈是离您单击的那个时间点最近的堆栈。 插入符用于将调用堆栈(显示在“当前”选项卡下)与采样时刻建立关联。 插入符号显示调用堆栈的确切位置,该位置是与用户选择的位置最近的调用堆栈。
解除堆栈阻塞
如果当前选定的线程元素表示阻止的段,该段稍后会在当前进程中的另一个线程取消阻止后开始执行,则执行取消阻止的线程的调用堆栈会显示在此选项卡上。
可见时间轴视图
线程阻塞视图的可见时间线剖面用于提供统计信息和报表链接。 放大、缩小、水平滚动、隐藏通道或显示通道时,活动图例中的数字将发生变化,以反映当前视图中的内容。 若要查看“图例”中某个项的报表,请单击该项。
缩放控件(线程视图)
缩放控件是一个滑块,可帮助你放大和缩小时间线,以便你可以专注于特定兴趣的区域。 由于此控件放大时间线视图的中心,因此在放大之前将感兴趣的区域居中。
在时间线视图中拖动以放大
通过拖动时间线视图放大可创建一个以黄色突出显示的区域。 释放鼠标按钮时,时间线视图会放大所选区域。
使用鼠标滚轮放大和缩小
单击时间线上的任意点(以确保其具有鼠标焦点),然后按 Ctrl 并移动鼠标滚轮(向前放大;向后放大)。