线程是作系统向其授予处理器时间的指令序列。 在作系统中运行的每个进程都至少包含一个线程。 具有多个线程的进程称为多线程。
具有多个处理器、多核处理器或超线程进程的计算机可以运行多个同时线程。 使用多个线程的并行处理可以极大地提高程序性能,但也可能使调试更加困难,因为你正在跟踪多个线程。
完美的并行处理并不总是可能的。 有时必须同步线程。 一个线程可能需要等待另一个线程的结果,或者一个线程可能需要对另一个线程正在使用的资源进行独占访问。 同步问题是多线程应用程序中 bug 的常见原因。 有时线程最终可能会等待永远不会变得可用的资源。 这会导致名为 死锁的条件。
线程和进程
线程 和 进程 是计算机科学的相关概念。 两者都表示必须按特定顺序执行的指令序列。 但是,单独的线程或进程中的说明可以并行执行。
作系统中存在进程,对应于用户视为程序或应用程序的内容。 另一方面,线程存在于进程中。 因此,线程有时称为 轻型进程。 每个进程由一个或多个线程组成。
存在多个进程可使计算机一次执行多个任务。 存在多个线程使进程能够并行执行工作。 在具有多处理器的计算机上,进程或线程可以在不同的处理器上运行。 这可实现真正的并行处理。
用于调试多线程应用的工具
Visual Studio 提供了用于调试多线程应用的不同工具。
对于线程,用于调试线程的主要工具是 “并行堆栈 ”窗口、 “并行监视 ”窗口、源窗口中的线程标记、 “线程” 窗口和 “调试位置” 工具栏。 若要了解并行堆栈中的“线程”视图,请参阅使用“线程”视图调试死锁。 若要了解如何使用 并行堆栈 和 并行监视 窗口,请参阅 开始调试多线程应用程序。 入门文章介绍如何使用线程标记。 另请参阅 演练:调试 C++ AMP 应用程序。
对于使用异步的 .NET 代码,用于调试的主要工具是并行堆栈窗口中的任务视图。 若要开始,请参阅调试异步应用程序(.NET)。
对于线程,用于调试线程的主要工具是 “并行堆栈 ”窗口、 “并行监视 ”窗口、源窗口中的线程标记、 “线程” 窗口和 “调试位置” 工具栏。 若要了解并行堆栈中的“线程”视图,请参阅“并行堆栈”窗口中的“查看线程和任务”。 若要了解如何使用 并行堆栈 和 并行监视 窗口,请参阅 开始调试多线程应用程序。 入门文章介绍如何使用线程标记。 另请参阅 演练:调试 C++ AMP 应用程序。
对于使用异步的 .NET 代码,用于调试的主要工具是并行堆栈窗口中的任务视图。 有关详细信息,请参阅在“并行堆栈”窗口中查看线程和任务。
对于 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 线程 窗口 | - 标志列,可在其中标记要特别注意的线程。 - 当前线程列,其中黄色箭头指示当前线程。 - “ 线程计数 ”列,该列显示同一位置的线程数。 - “行 ”列,显示每个线程组所在的代码行。 - “地址 ”列,其中显示每个线程组所在的指令地址。 - “位置” 列,它是地址代码中的位置。 - 状态 列,显示线程是活动还是被阻止。 - 磁贴 列,显示行中线程的磁贴索引。 |
- 更改为其他线程 - 显示特定的磁贴和线程 - 显示或隐藏列 - 按列排序 - 对线程进行分组 - 冻结或解冻线程 - 标记或取消标记线程 - 仅显示已标记的线程 |