并发可视化工具中的线程视图

“线程”视图在并发可视化工具中最详细且功能最丰富的视图。 在“线程”视图中,可识别在执行段期间执行代码的线程,并分析线程是否正在执行或由于同步、I/O 或其他原因而阻塞。 “线程”视图报告还分析调用堆栈树的执行和取消阻塞线程

当线程正在执行时,并发可视化工具会收集样本。 线程停止执行后,可视化工具将检查所有操作系统上下文切换事件的线程。 发生上下文切换的原因:

  • 线程在同步基元上受阻。
  • 线程的量程到期。
  • 线程进行了阻塞 I/O 请求。

并发可视化工具对线程和上下文切换事件分类,并在线程的调用堆栈中搜索已知阻塞 API。 它在“线程”视图的左下角的活动图例中显示线程类别。 在大多数情况下,可以通过检查与上下文切换事件对应的调用堆栈来确定阻塞事件的根本原因。

如果没有调用堆栈匹配,则并发可视化工具使用 Windows 提供的“等待”原因。 但是,Windows 类别可能基于实现详细信息,并且可能无法反映用户意图。 例如,Windows 会将在本机精简读取器/编写器锁上阻塞的等待原因报告为 I/O 而不是同步。

“线程”视图还显示线程之间的依赖关系。 例如,如果发现在同步对象上阻塞的线程,则可找到对其取消阻塞的线程。 可以检查调用堆栈中的取消阻塞线程(当该线程取消阻塞另一个线程时)。

可使用“线程”视图执行以下操作

  • 确定应用的用户界面 (UI) 在特定执行阶段中无响应的原因。
  • 确定在同步、I/O、页面错误和其他事件上阻塞所花的时间量。
  • 发现来自系统上执行的其他进程的干扰的程度。
  • 确定并行执行的负载平衡问题。
  • 找出可伸缩性未达到最优或可伸缩性不存在的原因。 例如,为什么可使用更多逻辑核心时,并行应用的性能不提高。
  • 了解应用中的并发度以帮助进行并行化。
  • 确定工作线程和执行的关键路径间的依赖关系。

使用“线程”视图

要启动并发可视化工具,请选择“分析”>“并发可视化工具”,然后选择一个选项,例如“启动新进程” 。

并发可视化工具会启动应用并收集跟踪,直到用户选择“停止收集”。 然后,可视化工具会分析跟踪并在跟踪报告页面上显示结果。

选择报告左上角的“线程”选项卡,打开“线程”视图

Threads view

选择时间间隔和线程,开始性能分析。

时间线分析

“线程”视图的上半部分是时间线。 时间线显示主计算机上的进程与所有物理磁盘设备中的所有线程的活动。 它还显示 GPU 活动和标记事件。

在时间线上,X 轴上是时间,y 轴上是几个通道:

  • 系统上每个磁盘驱动器两个 I/O 通道,一个通道用于读取,另一个用于写入。
  • 进程中每个线程一个通道。
  • 标记通道(如果跟踪中存在事件标记)。 标记通道最初出现在生成这些事件的线程通道下。
  • GPU 通道。

最初,线程按创建顺序进行排序,以便主应用线程处于第一位。 在“排序依据”下拉列表中选择另一个选项,以按另一种标准(例如“执行”)对线程进行排序

时间线的颜色指示线程在给定时间的状态。 绿色段表示已在执行,红色段指示已因同步而受阻,黄色段指示已被抢占,紫色段指示已参与设备 I/O。

可以放大以查看更多详细信息,也可以缩小以查看更长的时间间隔。 在图上选择段和点以获取有关类别、开始时间、延迟和调用堆栈状态的详细信息。

使用该时间线检查并行循环和并发任务所涉及的线程间的工作平衡。 如果某个线程完成所用时间要长于其他线程,则工作可能会不平衡。 可提高应用的性能,具体方法是在线程间更均匀地分布工作。

如果在某个时间点只有一个线程正在执行,则应用可能无法在系统上充分利用并发。 可以使用时间线关系图检查线程之间的依赖关系以及阻塞线程与被阻塞线程之间的临时关系。 若要重新排列线程,请选择一个线程,然后在工具栏上选择向上或向下按钮。

可隐藏未执行工作或完全受阻的线程,因为其统计信息不相关,可能会阻碍报告。 可隐藏线程,方法是选择线程名称,然后选择工具栏上的“隐藏所选线程”或“隐藏所选线程之外的所有线程”图标。 要识别要隐藏的线程,请选择左下角的“每线程摘要”链接。 可隐藏“每线程摘要”图中非活动状态的线程

线程执行详细信息

要获取有关执行段的更多详细信息,请选择时间线绿色段上的点。 可视化工具在所选点上方显示黑色脱字号,并在底部窗格的“当前”选项卡上显示其调用堆栈。 可以在执行段上选择多个点。

注意

若段的持续时间少于 1 毫秒,则并发可视化工具可能无法解析执行段上的选择。

若要获取当前所选时间范围内所有未隐藏线程的执行分析,请在左下方的图例中选择“执行”

线程阻塞详细信息

要获取有关线程上特定区域的信息,请将鼠标悬停在时间线上该区域的上方以显示工具提示。 工具提示包含类别、开始时间和延迟等信息。 选择区域以在底部窗格的“当前”选项卡中显示该时间点的调用堆栈。 该窗格还显示类别、延迟、阻塞 API(如果存在),以及取消阻塞线程(如果存在)。 通过检查调用堆栈,可以确定线程阻塞事件的基本原因。

执行路径可能有多个阻塞事件。 要通过阻塞类别进行检查并更快找到问题区域,请在左侧的图例中选择阻塞类别。

线程之间的依赖关系

并发可视化工具显示线程之间的依赖关系,以便用户确定受阻线程尝试执行的操作,以及其他线程使它可以执行的操作。

若要确定哪个线程对另一个线程取消了阻塞,请选择时间线上的阻塞段。 如果并发可视化工具可以确定取消阻塞线程,则它会在取消阻塞线程与跟在阻塞段之后的执行段之间绘制一条线。 在底部窗格中选择“取消阻塞堆栈”可以查看相关调用堆栈

分析报告

时间线图下方是一个窗格,其中包含“分析报告”、“当前”和“取消阻塞堆叠”报告选项卡。 更改时间线和线程选择时,报告会自动更新。 对于大型跟踪,在计算更新时,报告窗格可能暂时不可用。

“分析报告”选项卡

“分析报告”具有两个筛选器

  • 要筛选掉花费很少时间的调用树条目,请在“降噪目标”字段中键入介于 0 和 99% 之间的筛选值。 默认值为 2%。
  • 若要仅查看你的代码的调用树,请选中“仅我的代码”复选框。 若要查看所有调用树,请清除该复选框。

“分析报告”选项卡在图例中显示类别和链接的报告。 要显示报告,请选择左侧的其中一个条目:

  • 执行“执行”报表显示应用程序在执行过程所用的时间的明细

    若要查找在其中花费执行时间的代码行,请展开调用关系树,然后在调用树条目快捷菜单上,选择“查看源”或“查看调用站点”。 “查看源”会查找执行的代码行。 “查看调用站点”会查找调用执行的代码行。 如果只有一个调用站点行存在,则会突出显示其代码。 如果存在多个调用站点,请在对话框中选择所需的调用站点,然后选择“转至源”。 在查找具有最多实例和/或最大时间的调用站点时,这往往最为有用。 有关详细信息,请参阅执行分析报告

  • 同步“同步”报表显示对同步块负责的调用,以及每个调用堆栈的总阻塞时间。 有关详细信息,请参阅同步时间

  • I/O“I/O”报表显示对 I/O 块负责的调用,以及每个调用堆栈的总阻塞时间。 有关详细信息,请参阅 I/O 时间(线程视图)

  • 休眠“休眠”报表显示对休眠块负责的调用,以及每个调用堆栈的总阻塞时间。 有关详细信息,请参阅睡眠时间

  • 内存管理“内存管理”报表显示出现内存管理块的调用,以及每个调用堆栈的总阻塞时间。 使用此信息可以确定具有过多分页或垃圾回收问题的区域。 有关详细信息,请参阅内存管理时间

  • 抢占“抢占”报表显示其中的系统上进程抢占当前进程以及替换了当前进程中的线程的各个线程。 可以使用此信息确定对抢占负有最多责任的进程和线程。 有关详细信息,请参阅抢占时间

  • UI 处理“UI 处理”报表显示对 UI 处理块负责的调用,以及每个调用堆栈的总阻塞时间。 有关详细信息,请参阅 UI 处理时间

  • 每线程摘要 选择“每线程摘要”会显示一个图表,该图表显示当前所选时间间隔的线程状态。 此彩色编码列显示每个线程在运行、阻塞、I/O 和其他状态下所用的总时间。 线程在底部进行标记。 调整时间线关系图中的缩放级别时,此图会自动更新。

    在某些缩放级别,某些线程可能不会在图中显示。 发生这种情况时,省略号 (...) 会出现在右侧。 如果所需线程未出现,则可以隐藏其他线程。 有关详细信息,请参阅“每线程摘要”报告

  • Disk 操作 选择“Disk 操作”,可显示当前进程的磁盘 I/O 中涉及的进程和线程、它们使用的文件(例如,已加载的 DLL)、读取的字节数以及其他信息。 可以使用此报告评估在执行过程中用于访问文件的时间(尤其是在进程似乎受到 I/O 限制时)。 有关详细信息,请参阅“磁盘操作”报告

“当前”选项卡

此选项卡显示时间线关系图中某个线程段上的所选点的调用堆栈。 调用堆栈会进行修整以显示与应用相关的活动。

“取消阻塞堆栈”选项卡

此选项卡显示取消阻塞所选线程的线程以及取消阻塞调用堆栈。

通道(线程视图)

并发可视化工具显示四类通道:线程通道、磁盘通道、标记通道和 GPU 通道。

线程通道

线程通道通过颜色显示一个线程的状态。 停在通道名称处时,将显示给定线程的开始函数。 并发可视化工具可检测到多种线程。 下表列出了最常用的线程类型。

线程 描述
主线程 启动应用的线程。
工作线程 由应用程序主线程创建的线程。
CLR 工作线程 由公共语言运行时 (CLR) 创建的工作线程。
调试器帮助程序 由 Visual Studio 调试器创建的工作线程。
ConcRT 线程 由 Microsoft 并发运行时创建的线程。
GDI 线程 由 GDIPlus 创建的线程。
OLE/RPC 线程 作为 RPC 工作线程创建的线程。
RPC 线程 作为 RPC 线程创建的线程。
Winsock 线程 作为 Winsock 线程创建的线程。
线程池 由 CLR 线程池创建的线程。

磁盘通道

磁盘通道对应于计算机中的物理驱动器。 由于系统上的每个物理驱动器均存在单独读取和编写操作通道,因此每个驱动器均有两个通道。 磁盘数量对应于内核设备名称。 只有在磁盘上有活动时才显示磁盘通道。

标记通道

标记通道对应于由应用及其所使用的库生成的事件。 例如,任务并行库、并行模式库和 C++ AMP 生成显示为标记的事件。 每个标记通道关联一个线程 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)一起使用。

仅我的代码(线程视图)

如果选择此选项,将筛选调用堆栈,仅显示你的代码和一级被调用的函数。

通过激活此选项,可极大地降低调用堆栈的复杂性,还可以简化对特定问题的诊断。

在某些情况下,选择此选项可以筛选出阻止的调用。 如果需要完整的调用堆栈详细信息以便作决定,请清除此选项,公开完整的调用堆栈。

管理通道

在并发可视化工具的“线程视图”中,可以整理进程的各个通道,以便查看特定模式。 您可以将通道排序、上下移动,以及隐藏或显示这些通道。

排序依据

基于当前的缩放级别,您可以使用“排序依据”控件按不同的条件对线程排序。 在查找特定模式时,这特别有用。 您可以按照以下条件排序:

条件 定义
开始时间 按线程的开始时间排序。 这是默认的排序顺序。
结束时间 按线程的结束时间排序。
执行 按执行所用的时间百分比对线程排序。
同步 按同步所用的时间百分比对线程排序。
I/O 按 I/O(读取和写入数据)所用的时间百分比对线程排序。
睡眠状态 按休眠所用的时间百分比对线程排序。
Paging 按分页所用的时间百分比对线程排序。
优先 按抢占所用的时间百分比对线程排序。
UI 处理 按用户界面处理所用的时间百分比对线程排序。

上移或下移选定的通道

可以使用这些控件向上或向下移动列表中的通道。 例如,您可以将彼此相关的通道放在一起,以便帮助您检查特定的模式或跨线程关系。

将选定的通道移动到顶部或底部

您可以将选定的通道移动到列表的顶部或底部,以便检查特定的模式,或在检查某些通道时移开其他的通道。

隐藏选定的通道

如果要隐藏通道,请选择此控件。 例如,如果一个线程在您托管进程的生存期内 100% 同步,则可以在分析其他线程时隐藏该线程。

注意

隐藏某个线程时,还会将其从计算时间中移除,计算时间显示在活动图例和分析报告中。

显示所有通道

当一个或多个通道被隐藏时,此控件处于活动状态。 如果选择此控件,将显示所有隐藏的元素并将其全部添加回时间计算。

将标记移到顶部

如果跟踪包含标记事件,则可以使用此命令将标记通道移动到时间线的顶部。 它们的相对顺序将被保留。

按线程对标记进行分组

如果跟踪包含标记事件,则可以使用此命令,按照生成标记事件的线程对标记通道进行分组。 磁盘通道将被移动到列表的顶部,而 GPU 通道将被移动到底部。

打开/关闭度量模式

通过使用此工具,可以精确地测量时间线中的时间长度。 若要启用度量模式,请单击“度量”按钮(它具有一个标尺图标),然后在时间线中拖动。 注意在拖动时,指针下面的区域会用黄色突出显示,度量的时间显示在工具栏中该按钮的右侧。 拖动时动态计算此值,以便能够立即看到特定事件占用的时间。 释放鼠标按钮后,时间值保持可见。

可以重复执行度量过程,但只显示最新的度量操作。 再次单击“度量”按钮即可关闭度量模式。

降噪百分比

默认情况下,降噪百分比设置的值为 2。 调用树中只显示非独占时间百分比大于或等于此设置的项。 通过更改此设置,可以控制在调用树中显示的项数。 例如,如果将此值更改为 10,将只显示非独占时间百分比大于或等于 10% 的调用树项。 通过增大此设置的值,可以关注对进程性能影响较大的项。

基于可见时间范围的报表

“分析”视图显示基于当前可见的时间范围和通道的报表。 若要查看不同数据子集的详细信息,请单击图例中的相应项。

线程视图”报表中,可以找到有关表中数据的更多信息。

线程就绪连接器

单击阻止段以查看调用堆栈及其解除阻止的堆栈时,还可能会显示线程就绪连接器。 如果解除阻止的事件在当前进程中的另一个线程上发生,则线程就绪连接器可直观地标识允许阻止的线程继续执行的线程和执行段。

时间线插入符号

当在执行线程段的时间线上选择一个点时,它的上方将显示时间线插入符号。 在当前堆栈选项卡上显示的调用堆栈是在时间上与单击段的位置最接近的调用堆栈。 此插入符号用于将调用堆栈(显示在“当前”选项卡下方)与其采样时刻相关联。 插入符号显示调用堆栈的确切位置,即距离用户选择的位置最近的调用堆栈。

取消阻塞堆栈

如果当前选定的线程元素表示一个受阻片段,且该片段在被当前进程中的另一个线程取消阻塞后再开始执行,则此选项卡上将显示执行取消阻塞操作的线程的调用堆栈。

可见时间线分析

线程阻塞视图的可见时间线分析提供统计信息和报告的链接。 当进行放大、缩小、水平滚动屏幕、隐藏色条,或显示色条时,活动图例中的数字随之更改以反映当前视图中的内容。 若要查看有关图例中某个项的报告,请单击此项。

缩放控件(线程视图)

缩放控件是一个滑块,用于在时间线上进行放大和缩小操作,以便于你关注特定的感兴趣的区域。 因为此控件放大时间线视图的中心位置,所以在放大之前将感兴趣的区域移到中心位置。

在时间线视图中通过拖动进行放大

在时间线视图中通过拖动进行放大可以创建以黄色突出显示的区域。 释放鼠标按钮后,时间线视图会放大选定的范围。

通过使用鼠标滚轮来放大和缩小

单击时间线上的任意点(以确保具有鼠标焦点),然后按 Ctrl 并滚动鼠标滚轮(向前滚为放大;向后滚为缩小)。