在 Visual Studio 中调试多线程应用程序

线程是作系统向其授予处理器时间的指令序列。 在作系统中运行的每个进程都至少包含一个线程。 具有多个线程的进程称为多线程。

具有多个处理器、多核处理器或超线程进程的计算机可以运行多个同时线程。 使用多个线程的并行处理可以极大地提高程序性能,但也可能使调试更加困难,因为你正在跟踪多个线程。

完美的并行处理并不总是可能的。 有时必须同步线程。 一个线程可能需要等待另一个线程的结果,或者一个线程可能需要对另一个线程正在使用的资源进行独占访问。 同步问题是多线程应用程序中 bug 的常见原因。 有时线程最终可能会等待永远不会变得可用的资源。 这会导致名为 死锁的条件。

线程和进程

线程进程 是计算机科学的相关概念。 两者都表示必须按特定顺序执行的指令序列。 但是,单独的线程或进程中的说明可以并行执行。

作系统中存在进程,对应于用户视为程序或应用程序的内容。 另一方面,线程存在于进程中。 因此,线程有时称为 轻型进程。 每个进程由一个或多个线程组成。

存在多个进程可使计算机一次执行多个任务。 存在多个线程使进程能够并行执行工作。 在具有多处理器的计算机上,进程或线程可以在不同的处理器上运行。 这可实现真正的并行处理。

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

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

  • 对于 GPU 上的调试线程,主工具是 “GPU 线程” 窗口。 请参阅 “如何:使用 GPU 线程”窗口

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

Visual Studio 还提供强大的断点和跟踪点,这在调试多线程应用程序时非常有用。 使用断点条件和筛选器在单个线程上放置断点。 利用跟踪点,可以跟踪程序的执行而不中断,以研究死锁等问题。 有关详细信息,请参阅 断点作和跟踪点

调试具有用户界面的多线程应用程序可能特别困难。 可以考虑在第二台计算机上运行应用程序并使用远程调试。 有关详细信息,请参阅 远程调试

下表显示了可用信息,以及可在以下每个位置执行的作:

用户界面 可用信息 可以执行的作
“附加到进程 ”对话框 可附加到的可用进程:

- 进程名称(.exe)
- 进程 ID 号
- 菜单栏标题
- 类型(托管 v4.0;托管 v2.0、v1.1、v1.0;x86;x64;IA64)
- 用户名(帐户名)
- 会话编号
选择要附加到的进程

选择远程计算机

更改连接到远程计算机的传输类型
“进程” 窗口 附加的进程:

- 进程名称
- 进程 ID 号
- 处理 .exe 的路径
- 菜单栏标题
- 状态 (中断。正在运行)
- 调试(本机、托管等)。
- 传输类型(默认,本机无身份验证)
- 传输限定符 (远程计算机)
工具:

-附加
-分离
-终止

快捷菜单:

-附加
-分离
- 调试停止时分离
-终止
调试位置 工具栏 - 当前进程
- 暂停应用程序
- 恢复应用程序
- 暂停和关闭应用程序
- 当前线程
- 切换当前线程标志状态
- 仅显示已标记的线程
- 仅显示当前进程
- 当前堆栈帧
- 切换到另一个进程
- 暂停、恢复或关闭应用程序
- 切换到当前进程中的另一个线程
- 切换到当前线程中的另一个堆栈帧
- 标记或取消标记当前线程
- 仅显示已标记的线程
- 仅显示当前进程
“并行堆栈” 窗口 - 一个窗口中多个线程的调用堆栈。
- 每个线程的活动堆栈帧。
- 任何方法的调用方和被调用方。
- 死锁检测
- 筛选掉指定的线程
- 筛选出外部代码堆栈
- 切换到“任务”视图
- 标记或取消标记线程
-缩放
- 复制堆栈帧
- 将所有堆栈保存/导出为图像
“并行监视 ”窗口 - 标志列,可在其中标记要特别注意的线程。
- 框架列,其中箭头指示所选框架。
- 可显示计算机、进程、磁贴、任务和线程的可配置列。
- 标记或取消标记线程
- 仅显示已标记的线程
- 切换帧
- 对列进行排序
- 对线程进行分组
- 冻结或解冻线程
- 在“并行监视”窗口中导出数据
“线程” 窗口 当前进程中的线程:

- 线程 ID
- 托管 ID
- 类别(主线程、接口线程、远程过程调用处理程序或工作线程)
- 线程名称
- 创建线程的位置
-优先权
- 相关性掩码
- 挂起计数
- 进程名称
- 标志指示器
- 挂起的指示器
工具:

-搜索
- 搜索调用堆栈
- 标记仅我的代码
- 标记自定义模块选择
- 分组依据
-列
- 展开/折叠调用堆栈
- 展开/折叠组
- 冻结/解冻线程

快捷菜单:

- 在源中显示线程
- 切换到线程
- 冻结正在运行的线程
- 解冻冻结线程
- 标记线程以供其他研究
- 取消标记线程
- 重命名线程
- 显示和隐藏线程

其他作:

- 查看 DataTip 中线程的调用堆栈
源窗口 左侧装订线中的线程指示器指示单个或多个线程(默认情况下关闭,在 “线程” 窗口中使用快捷菜单打开) 快捷菜单:

- 切换到线程
- 标记线程以供其他研究
- 取消标记线程
“任务” 窗口 - 查看有关 Task 对象的信息,包括任务 ID、任务状态(计划、正在运行、等待、死锁),以及分配给任务的线程。
- 调用堆栈中的当前位置。
- 在创建时传递给任务的委托
- 切换到当前任务
- 标记或取消标记任务
- 冻结或解冻任务
GPU 线程 窗口 - 标志列,可在其中标记要特别注意的线程。
- 当前线程列,其中黄色箭头指示当前线程。
- “ 线程计数 ”列,该列显示同一位置的线程数。
- “行 ”列,显示每个线程组所在的代码行。
- “地址 ”列,其中显示每个线程组所在的指令地址。
- “位置” 列,它是地址代码中的位置。
- 状态 列,显示线程是活动还是被阻止。
- 磁贴 列,显示行中线程的磁贴索引。
- 更改为其他线程
- 显示特定的磁贴和线程
- 显示或隐藏列
- 按列排序
- 对线程进行分组
- 冻结或解冻线程
- 标记或取消标记线程
- 仅显示已标记的线程