演练:使用“线程”窗口调试多线程应用(C#、Visual Basic、C++)
多个 Visual Studio 用户界面元素可帮助调试多线程应用。 本文介绍了代码编辑器窗口、“调试位置”工具栏和“线程”窗口中的多线程调试功能。 有关用于调试多线程应用的其他工具的信息,请参阅开始调试多线程应用。
完成本教程只需几分钟的时间,可帮助你熟悉调试多线程应用的基础知识。
创建一个多线程应用项目
创建要在本教程中使用的以下多线程应用项目:
打开 Visual Studio 并创建一个新项目。
如果开始窗口未打开,请选择“文件”>“开始窗口” 。
在“开始”窗口上,选择“新建项目”。
在“开始”窗口上,选择“创建新项目” 。
在“创建新项目”窗口的搜索框中输入或键入“控制台”。 接下来,从“语言”列表中选择“C#”或“C++”,然后从“平台”列表中选择“Windows” 。
应用语言和平台筛选器之后,对 .NET Core、NET 5+ 或 C++ 选择“控制台应用”,然后选择“下一步”。
注意
如果没有看到正确的模板,请转到“工具”>“获取工具和功能...”,这会打开 Visual Studio 安装程序 。 选择“.NET 桌面开发”或“使用 C++ 的桌面开发”工作负载,然后选择“修改” 。
在“配置新项目”窗口中,在“项目名称”框中键入名称或使用默认名称 。 然后,选择“下一步”或“创建”(视具体提供的选项而定)。
对于 .NET Core 或 .NET 5+,选择建议的目标框架或 .NET 8,然后选择“创建”。
新的控制台项目随即显示。 创建该项目后,将显示源文件。 根据所选的语言,源文件的名称可能是 Program.cs 或 MyThreadWalkthroughApp.cpp。
将源文件中的代码替换为开始调试多线程应用中的 C# 或 C++ 示例代码。
选择“文件”>“全部保存”。
“启动调试”
在源代码中找到以下几行:
在
Console.WriteLine();
行上设置断点,方法是在左滚动条槽中进行单击,或选择该行并按 F9。断点在代码行旁边的左滚动条槽中显示为红色圆圈。
选择“调试”>“开始调试”,或按 F5。
应用在调试模式下启动,在断点处暂停。
处于中断模式时,通过选择“调试”>“窗口”>“线程”打开“线程”窗口。 必须处于调试会话中才能打开或查看“线程”和其他调试窗口。
检查线程标记
在源代码中,找到
Console.WriteLine();
行。- 在“线程”窗口中单击右键,然后从菜单中选择“在源中显示线程”。
源代码行旁边的滚动条槽现在显示线程标记图标 。 线程标记指示线程在此位置停止。 如果该位置有多个已停止的线程,则会显示 图标。
将指针悬停在线程标记上。 此时会出现一个数据提示,显示已停止的线程的名称和线程 ID 号。 线程名称可能是
<No Name>
。提示
为了帮助识别无名称的线程,可以在“线程”窗口中将其重命名。 右键单击线程,然后选择“重命名”。
右键单击源代码中的线程标记,以查看快捷菜单上的可用选项。
标记线程和取消标记线程
可以标记线程,以跟踪需要特别注意的线程。
从源代码编辑器或“线程”窗口中标记和取消标记线程。 从“调试位置”或“线程”窗口工具栏中选择是仅显示已标记的线程,还是显示所有线程。 从任何位置进行的选择都会影响所有位置。
在源代码中标记和取消标记线程
通过选择“查看”>“工具栏”>“调试位置”,打开“调试位置”工具栏。 也可以在工具栏区域中单击右键,然后选择“调试位置”。
“调试位置”工具栏有三个字段:“进程”、“线程”和“堆栈帧”。 下拉“线程”列表,记下有多少个线程。 在“线程”列表中,当前正在执行的线程由 > 符号标记。
在源代码窗口中,将鼠标悬停在滚动条槽中的线程标记图标上,然后在数据提示中选择旗形图标(或空旗形图标之一)。 旗形图标变为红色。
也可以右键单击线程标记图标,指向“标记”,然后从快捷菜单中选择要标记的线程。
在“调试位置”工具栏上,选择“线程”字段右侧的“仅显示已标记的线程”图标 。 除非标记了一个或多个线程,否则该图标显示为灰色。
现在,只有已标记的线程出现在工具栏的“线程”下拉列表中。 若要再次显示所有线程,请再次选择“仅显示已标记的线程”图标。
提示
在标记一些线程后,可以将光标置于代码编辑器中,单击右键,然后选择“将已标记的线程运行到光标处”。 请确保选择所有已标记的线程将达到的代码。 “将已标记的线程运行到光标处”会在所选代码行上暂停线程,这样就可以通过冻结和解冻线程更轻松地控制执行顺序。
若要切换当前正在执行的线程的已标记或未标记状态,请选择“仅显示已标记的线程”按钮左侧的单个旗形“切换当前线程标记状态”工具栏按钮。 当仅显示已标记的线程时,标记当前线程对于查找当前线程非常有用。
若要取消标记线程,请将鼠标悬停在源代码中的线程标记上,然后选择红旗图标将其清除,或者右键单击线程标记并选择“取消标记”。
在“线程”窗口中标记和取消标记线程
在“线程”窗口中,已标记的线程旁边带有红旗图标,而未标记的线程(如果显示)带有空轮廓图标。
选择旗形图标可根据线程的当前状态将其状态更改为已标记或未标记。
也可以右键单击某行,然后从快捷菜单中选择“标记”、“取消标记”或“取消标记所有线程”。
“线程”窗口工具栏还有一个“仅显示已标记的线程”按钮,即两个旗形图标中右边的那个。 它的作用与“调试位置”工具栏上的按钮相同,这两个按钮中的任何一个都可控制这两个位置中显示的内容。
其他“线程”窗口功能
在“线程”窗口中,选择任意列的标题可按该列对线程进行排序。 再次选择可反转排序顺序。 如果显示所有线程,则选择旗形图标列将按已标记或未标记状态对线程进行排序。
“线程”窗口的第二列(无标题)为“当前线程”列。 此列中的黄色箭头表示当前执行点。
“位置”列显示每个线程在源代码中出现的位置。 选择“位置”条目旁边的展开箭头,或将鼠标悬停在该条目上方,可显示该线程的部分调用堆栈。
提示
有关线程调用堆栈的图形视图,请使用并行堆栈窗口。 若要在调试时打开该窗口,请选择“调试”>“窗口”>“并行堆栈”。
除了“标记”、“取消标记”和“取消标记所有线程”外,“线程”窗口项的右键单击上下文菜单中还有:
- “在源中显示线程”按钮。
- “十六进制显示”,它将“线程”窗口中的“线程 ID”从十进制格式更改为十六进制格式。
- 切换到线程,它会立即切换为执行该线程。
- “重命名”,允许更改线程名称。
- 冻结和解冻命令。
冻结和解冻线程执行
可以通过冻结和解冻或者暂停和恢复线程来控制线程执行工作的顺序。 冻结和解冻线程有助于解决并发问题,例如死锁和争用条件。
提示
若要在不冻结其他线程的情况下跟踪单个线程(这也是一种常见的调试方案),请参阅开始调试多线程应用程序。
冻结和解冻线程:
在“线程”窗口中右键单击任意线程,然后选择“冻结” 。 “当前线程”列中的“暂停”图标指示该线程已冻结。
在“线程”窗口工具栏中选择“列”,然后选择“挂起项计数”以显示“挂起项计数”列。 冻结线程的挂起项计数值为 1。
右键单击冻结的线程,然后选择“解冻”。
“暂停”图标消失,“挂起项计数”值变为 0。
切换到另一个线程
当你尝试切换到另一个线程时,可能会看到“应用程序处于中断模式”窗口。 该窗口指示线程没有当前调试器可以显示的任何代码。 例如,你可能正在调试托管代码,但线程是本机代码。 该窗口会提供解决此问题的建议。
切换到另一个线程:
在“线程”窗口中,记下当前线程 ID,即“当前线程”列中带有黄色箭头的线程。 你需要切换回该线程以继续运行应用。
右键单击其他线程,然后从上下文菜单中选择“切换到线程”。
请注意,“线程”窗口中的黄色箭头位置发生了变化。 原始的当前线程标记也保留为轮廓线。
查看代码源编辑器中线程标记上的工具提示,以及“调试位置”工具栏上“线程”下拉列表中的列表。 请注意,这些位置中的当前线程也发生了变化。
在“调试位置”工具栏上,从“线程”列表中选择其他线程。 请注意,另外两个位置中的当前线程也会发生变化。
在源代码编辑器中,右键单击线程标记,指向“切换到线程”,然后从列表中选择另一个线程。 请注意,这三个位置中的当前线程都会发生变化。
使用源代码中的线程标记,只能切换到在该位置停止的线程。 使用“线程”窗口和“调试位置”工具栏可以切换到任何线程 。
你现在已经了解了调试多线程应用的基础知识。 你可以使用“线程”窗口、“调试位置”工具栏中的“线程”列表或源代码编辑器中的线程标记,来观察、标记和取消标记以及冻结和解冻线程。