| 2011年12月18日 07:00 | |
2.4 Thread Checker线程检查器
1.1.1 计算机与微处理器
Intel(R) 线程检查器可以快速查找和修复Windows和OpenMP*线程软件中的bug。它监控程序执行过程中的线程行为,发现其中存在的竞争现象、线程阻塞以及潜在的线程死锁问题,提示同线程错误相关的源代码位置、侵权变量以及堆栈跟踪等。新版增添了从Windows主机系统分析Linux系统中运行的线程代码的功能。
Intel(R) 线程检查器含有一个错误检测引擎,可以在数小时内定位难于发现的线程错误,这些错误有时使用传统的工具或软件根本就发现不了。该软件在程序执行期间监控其线程行为,能够检测多种类型的线程错误。在对软件线程化时,最容易引入线程错误,而且难于发现。该情况下一类通常的错误是数据竞争现象,该现象是由于两个或多个线程同时访问相同的内存地址,而且至少一个线程需要对该地址中的内容进行更新引起的。因此数据竞争现象会导致不可预测的结果。Intel Thread Checker可以发现数据竞争现象,另外,它还能发现线程死锁和阻塞问题。
Intel(R) 线程检查器支持Intel(R) 64 与 Microsoft Visual Studio* 环境,通常集成到Intel(R) VTune性能分析器中使用。
2.4.1 线程检查器功能与使用
集成应用于Microsoft Visual Studio .NET*开发环境;
单击诊断功能可以帮助显示可能的错误原因并提出相关的解决建议;
自动错误检测,它可以创建足够的测试来发现通常、异常以及间断性线程bug;
竞争现象、线程死锁和阻塞检测,可以发现最微小的并行程序问题;
兼容于Win32* APIs for threads、C运行时间库函数和OpenMP;
检测难以发现的错误,通过错误检测引擎可以识别运行代码中的潜在bug,即使在程序分析过程中这些bug没有出现。用传统的调试器一般不易发现这些bug;
源代码行bug隔离--准确显示导致bug的变量,线程应用该变量的位置,变量定义位置以及侵权代码行的调用堆栈。
Intel(R) 线程检查器可找出所有潜在及可能发生的线程错误(如数据竞跑以及死锁情况),可跟踪至错误发生的源代码行及内存地址,如图2.10、2.11、2.12所示:
图2.8 Intel(R) 线程检查器可轻松查找线程错误
图2.9 深入到源代码内部以解决线程问题
图2.10 返回Intel(R) 线程检查器来深入查找线程错误
Intel(R) 线程检测器 3.0 Windows版兼容现今的行业标准开发工具:
Microsoft Visual Studio .NET* 开发环境
Microsoft Visual C++* .NET 编译器 2005、2003、2002 版或 Visual C++ 6.0
Intel(R) VTune性能分析器 7.2 或 8.0
Intel(R) Fortran 和 C++ 编译器
Windows 线程和 POSIX* 线程
支持 OpenMP
2.4.2 线程检查器实验
本实验学习如何使用Intel(R) 线程检查器来调试Win32*多线程程序并解决数据竞争、死锁问题。
(1)寻找潜在的数据竞争
编译和运行Potential程序的单线程版本
打开\code\Thread Checker\potential_serial文件夹,双击potential_serial.sln文件;
在Build菜单里选择Configuration Manager,然后选择Debug模式;
在Build菜单里选择Build Solution,编译相关文件;
在Debug菜单里选择Start Without Debugging,运行程序。
编译和运行Potential程序的多线程版本
打开\code\Thread Checker\potential_win文件夹,双击potential_win.sln文件;
在Build菜单里选择Configuration Manager,然后选择Debug模式;
按如下方式配置项目属性:
选中Debug模式 (/Zi)
链接时保留Debug信息 (/DEBUG)
禁止自动优化(/Od)
使用线程安全系统库 (/MDd)
使用二进制文件可重定位功能 (/fixed:no)
在Build菜单里选择Build Solution,编译相关文件;
在Debug菜单里选择Start Without Debugging,运行程序;
注:Potential程序的多线程版本中粒子数量和循环次数大幅度减少,以方便Intel(R) 线程检查器运行。
运行Intel(R) VTune性能分析器;
点击New Project;
在Category栏选择Threading Wizards,在下拉框中选择Intel Thread Checker Wizard;
选择刚才编译好的可执行文件路径(\code\Thread Checker\potential_win\Debug\potential_win.exe),点击Finish按钮,开始运行线程检查器;
线程检查器分析完毕后会显示一些诊断报告,双击可观察相应代码;
问题:
为什么会出现这些冲突?
(2)解决数据竞争问题
解决前一个实验里Intel(R) 线程检查器诊断出来的那些线程错误。哪些变量可以作为线程中的共享变量?哪些变量应该作为每个线程的私有变量?哪些变量出于线程同步的考虑,必须保护起来?
修改好代码中的错误后,重新编译运行,并用线程检查器检测,直到没有诊断错误为止;
当消灭代码中的线程问题后,把粒子和循环数量改为之前单线程版本的数量,重新编译运行。
问题:
修改好的多线程程序运行结果和之前的单线程程序一样吗?
(3)检测死锁问题
打开\code\Thread Checker\Deadlock\文件夹,双击Deadlock.sln文件;
在Build菜单里选择Configuration Manager,然后选择Debug模式;
按照上一小节实验里的方法配置项目属性;
在C/C++菜单中,选择Command Line,填入/Qtcheck,如图所示;
在Build菜单里选择Build Solution,编译相关文件;
在Debug菜单里选择Start Without Debugging,运行程序。
问题:
运行结果正确吗?为什么?
用线程检查器分析程序
运行Intel(R) 线程检查器;
点击New Project;
在Category栏选择Threading Wizards,在下拉框中选择Intel Thread Checker Wizard;
选择刚才编译好的可执行文件路径(\code\ Thread Checker\Deadlock\Debug\Deadlock.exe),点击Finish按钮,开始运行线程检查器;
运行结果可能是下面两张图其中一张,因为死锁并不一定每次都发生,如图2.13、2.14所示:
图2.11 诊断报告显示死锁并未发生,但线程检查器仍然检测出潜在的死锁风险,图中用黄色标出
图2.12 线程检查器检测到了死锁发生并列出来,此时用户需要强制关闭程序运行的DOS窗口
双击诊断列表中的任意一条(错误或者警告),研究相应代码和错误或警告发生的原因。
改正程序错误
返回到Microsoft Visual Studio界面,修改、重新编译程序;
提示:注意每个线程函数work0()和work1()中临界区变量cs0和cs1的调用顺序。
重新编译程序后,在VTune性能分析器界面,点击Activity菜单,选择Run,重新用线程分析器分析程序,检测是否有线程错误。
提示:只要对程序做细微改动即可解决所有线程问题,上一级的Solutions目录下提供了正确的DeadlockSolution.cpp程序作为参考。
问题:
用大的数据集可以让线程检查器收集到更多的信息,这个说法正确吗?
线程错误只需要用同步对象就能完全解决,这个说法正确吗?
当程序中存在死锁时,每次运行都会发生死锁吗?

