Visual Studio调试器指南---多线程应用程序调试(一)

线程是操作系统向其授予处理器时间的指令序列。 在操作系统中运行的每个进程都包含至少一个线程。 包含多个线程的进程称为多线程。有多个处理器、多核处理器或超线程进程的计算机可以同时运行多个线程。 使用多个线程的并行处理可以极大地提高程序性能,但也可能导致调试变得更加困难,因为正在跟踪多个线程。

多线程处理可能会引入新类型的潜在 bug。 例如,两个或多个线程可能需要访问同一资源,但是一次只能有一个线程可以安全地访问该资源。 需要某种形式的互斥才能确保每次只有一个线程访问该资源。 如果未正确实现互斥,则可能会创建不会执行任何线程的死锁情况。 死锁通常是一个难以调试的问题。

用于调试多线程应用的工具

Visual Studio 提供不同的工具用于调试多线程应用程序。

  • 对于线程,调试线程的主要工具有 "线程" 窗口、源窗口中的线程标记、"并行堆栈" 窗口、"并行监视" 窗口和 "调试位置" 工具栏。

  • 对于使用任务并行库(TPL)或并发运行时的代码,用于调试的主要工具是 "并行堆栈" 窗口、"并行监视" 窗口和 "任务" 窗口,该窗口还支持JavaScript. 

  • 对于调试 GPU 上的线程,主要工具是“GPU 线程”窗口。

  • 对于进程,主要工具是“附加到进程”对话框、“进程”窗口和“调试位置”工具栏。

Visual Studio 还提供功能强大的断点和跟踪点,在调试多线程应用程序时,这会很有用。 使用断点条件和筛选器将断点置于单个线程上。 使用跟踪点可以在不中断的情况下跟踪程序的执行,从而研究死锁之类的问题。

调试具有用户界面的多线程应用程序可能会特别困难。 可以考虑在另一台计算机上运行应用程序并使用远程调试。 

使用 "线程" 窗口调试多线程应用

多个 Visual Studio 用户界面元素可帮助调试多线程应用。 下面介绍 "代码编辑器" 窗口、"调试位置" 工具栏和 "线程" 窗口中的多线程调试功能。 

“启动调试”

  1. 代码行上设置断点,方法是单击左侧的滚动条线,或选择线条并按F9。断点在代码行旁边的左侧滚动条中显示为红色圆圈。

  2. 选择 "调试" > "开始调试",或按F5。

    应用程序在调试模式下启动,并在断点处暂停。

  3. 在中断模式下,通过选择 "调试" > Windows > 线程打开 "线程" 窗口。 你必须在调试会话中才能打开或查看线程和其他调试窗口。

检查线程标记

  1. "线程" 窗口中右键单击,然后选择菜单中的 "在源中显示线程"

    源代码行旁边的装订线现在显示一个线程标记图标线程标记线程标记指示线程在此位置停止。 如果该位置有多个已停止的线程,则会显示多个线程图标。

  2. 将指针悬停在线程标记上。 显示数据提示,并显示已停止的线程或线程的名称和线程 ID 号。 线程名称可能 <No Name>

    为了帮助识别不需要的线程,您可以在 "线程" 窗口中重命名它们。 右键单击该线程,然后选择 "重命名"。

  3. 右键单击源代码中的线程标记可查看快捷菜单上的可用选项。

标记线程和取消标记线程

您可以标记线程以跟踪您要特别注意的线程。

在源代码编辑器或 "线程" 窗口中标记和取消标记线程。 从 "调试位置" 或 "线程" 窗口工具栏中选择是仅显示标记的线程还是显示所有线程。 从任何位置进行的选择将影响所有位置。

在源代码中标记和取消标记线程

  1. 通过选择 "视图" > 工具栏 > 调试位置,打开 "调试位置" 工具栏。 还可以在工具栏区域中右键单击,然后选择 "调试位置"。

  2. "调试位置" 工具栏有三个字段: "进程"、"线程" 和 "堆栈帧"。 下拉线程列表,并记下有多少线程。 在线程列表中,当前正在执行的线程由 > 符号标记。

  3. 在源代码窗口中,将鼠标悬停在滚动条中的一个线程标记图标上,并在数据提示中选择标志图标(或一个空标志图标)。 标志图标变为红色。

    您还可以右键单击线程标记图标,指向 "标志",然后从快捷菜单中选择要标记的线程。

  4. 在 "调试位置" 工具栏上,选择 "仅显示标记的线程" 图标在 "线程" 字段的右侧显示标记的线程除非标记一个或多个线程,否则图标为灰显。

    只有已标记的线程才会出现在工具栏的 "线程" 下拉列表中。 若要再次显示所有线程,请再次选择 "仅显示标记的线程" 图标。

    提示

    标记了某些线程后,可以将光标放在代码编辑器中,右键单击,然后选择 "将标记的线程运行到光标处"。 请确保选择所有已标记的线程将达到的代码。 将标记的线程运行到光标处将暂停选定代码行上的线程,从而可以更轻松地通过冻结和解冻线程控制执行顺序。

  5. 若要切换当前正在执行的线程的已标记或未标记状态,请选择 "仅显示标记的线程" 按钮左侧的单个标志 "切换当前线程标记状态" 工具栏按钮。 标记当前线程对于仅显示标记的线程时查找当前线程非常有用。

  6. 若要取消标记线程,请将鼠标悬停在源代码中的线程标记上,并选择红色标记图标以清除它,或右键单击线程标记,然后选择 "取消标记"。

在 "线程" 窗口中标记和取消标记线程

在 "线程" 窗口中,已标记的线程旁边显示红色标志图标,而未标记的线程(如果已显示)具有空图标。

“线程”窗口

选择标记图标,以根据其当前状态将线程状态更改为标记或未标记。

还可以右键单击行,然后从快捷菜单中选择 "标记"、"取消标记" 或 "取消标记所有线程"。

"线程" 窗口工具栏还具有 "仅显示标记的线程" 按钮,它是两个标志图标中的 righthand。 它的工作方式与 "调试位置" 工具栏上的按钮相同,其中的一个按钮控制两个位置的显示。

其他线程窗口功能

在 "线程" 窗口中,选择任意列的标头以按该列对线程进行排序。 再次选择以反转排序顺序。 如果显示了所有线程,则选择标志图标列会按标记或未标记的状态对线程进行排序。

"线程" 窗口的第二列(没有标头)是当前线程列。 此列中的黄色箭头标记当前执行点。

"位置" 列显示每个线程在源代码中出现的位置。 选择Location项旁边的展开箭头,或将鼠标悬停在该项上,以显示该线程的部分调用堆栈。

有关线程调用堆栈的图形视图,请使用 "并行堆栈" 窗口。 若要在调试时打开窗口,请选择 "调试"> Windows > "并行堆栈"。

除了标记 、取消标记和取消标记所有线程外,线程窗口项的右键单击上下文菜单还具有:

  • "在源中显示线程" 按钮。
  • 十六进制显示,将 "线程" 窗口中的线程 ID更改为十进制格式。
  • 切换到线程,这会立即将执行切换到该线程。
  • 重命名,使你可以更改线程名称。
  • 冻结和解冻命令。

冻结和解冻线程执行

可以冻结和解冻线程,也可以挂起和恢复线程,以控制线程执行工作的顺序。 冻结和解冻线程可帮助解决并发性问题,如死锁和争用条件。

提示

若要在不冻结其他线程的情况下跟踪单个线程,这也是一种常见的调试方案,请参阅开始调试多线程应用程序。

冻结和解冻线程:

  1. 在 "线程" 窗口中,右键单击任意线程,然后选择 "冻结"。 "当前线程" 列中的暂停图标指示线程已冻结。

  2. 选择 "线程" 窗口工具栏中的列,然后选择 "挂起的计数" 以显示 "挂起的计数" 列。 冻结线程的挂起计数值为1。

  3. 右键单击冻结的线程,然后选择 "解冻"。

    暂停图标消失,"挂起的计数" 值更改为0。

切换到另一个线程

尝试切换到另一个线程时,可能会看到应用程序处于中断模式窗口。 此窗口告诉您该线程没有当前调试器可以显示的任何代码。 例如,你可能正在调试托管代码,但线程是本机代码。 此窗口提供了解决此问题的建议。

切换到另一个线程:

  1. 在 "线程" 窗口中,记下当前线程 ID (当前线程列中带有黄色箭头的线程)。 你需要切换回此线程以继续运行你的应用程序。

  2. 右键单击其他线程,然后从上下文菜单中选择 "切换到线程"。

  3. 观察 "线程" 窗口中的黄色箭头位置是否已更改。 原始的当前线程标记也保留为轮廓。

    查看代码源编辑器中的线程标记上的工具提示,以及 "调试位置" 工具栏上的 "线程" 下拉列表中的列表。 观察当前线程是否也已更改。

  4. 在 "调试位置" 工具栏上,从 "线程" 列表中选择一个不同的线程。 请注意,当前线程还会在其他两个位置发生更改。

  5. 在源代码编辑器中,右键单击线程标记,指向 "切换到线程",然后从列表中选择另一个线程。 观察当前线程是否在所有三个位置发生了更改。

在源代码中,通过线程标记,只能切换到在该位置停止的线程。 使用“线程”窗口和“调试位置”工具栏可以切换到任何线程。

你现在已经了解了调试多线程应用程序的基础知识。 您可以使用 "线程" 窗口、"调试位置" 工具栏中的 "线程" 或 "源代码编辑器" 中的 "线程" 标记来观察、标记和取消标记线程,以及冻结和解冻线程。

 

posted on 2020-03-17 08:37  活着的虫子  阅读(10693)  评论(1编辑  收藏  举报

导航