`
jgsj
  • 浏览: 963674 次
文章分类
社区版块
存档分类
最新评论

C++多线程调试和测试的注意事项

 
阅读更多

转自:http://blog.csdn.net/program_think/archive/2009/04/10/4061801.aspx

 在上次的帖子 聊了C++多线程的跨平台问题,后来感觉意犹未尽。今天顺便说一下开发C++多线程应用程序时,有关调试和测试的一些注意事项。下面这些注意事项主要是针对C++,不过有些对于其它的语言也适用。

  ★关于设置断点和单步执行
  很多同学非常依赖于调试器的断点功能和单步功能。这在单线程情况下倒还好(不过有些单线程但涉及GUI的程序,也会有点麻烦)。至于多线程程序的调试,这两种手段简直就是噩梦的开始。多线程造成的主要问题大都和竞态条件(Race Condition,详细解释看“这里 ”)有关。而设置断点或单步跟踪可能会严重干扰 多线程之间的竞争状态。导致你看到的是一个假象。比如本来有两个线程并发执行,存在某些不和谐的Bug(由竞态引起)。一旦你在某一个线程设置了断点,该线程在断点处停住了,只剩下另一个线程在跑。这时候,并发的场景已经完全被破坏了,你通过调试器看到的可能 是一个和谐的场景。
  稍微跑一下题。这很类似量子力学的“测不准原理”,观测者的观测行为干扰了被测量的客体,导致观测者看到的是一个干扰后的现象。

  ★关于Log输出
  既然断点和单步不好用。那咋办捏?一个替代方案是输出log日志。它可以有效减轻断点和单步所导致的(针对竞态条件的)副作用。
  ◇传统Log机制的问题
  传统的log输出主要是打印到屏幕或者输出到文件。对于C++而言,标准库内置的类和函数(比如cout、printf、fputs)可能会有线程安全的问题(和编译器的具体实现有关)。尤其是标准流类库(iostream)的八个全局对象,更是要小心慎用。轻则输出的log文本混杂,重则导致程序崩溃。
  鉴于上述原因,应该尽量使用第三方线程库内置的log机制来搞定log输出功能。比如ACE内置的ACE_Log_Msg等。
  ◇Log函数要短小精悍
  很多情况下,我们会包装一个公用的函数来实现log输出功能。然后在该函数内部调用线程库的log类/函数。为了不影响线程的竞态条件,这个log函数要尽可能简单轻便:不要涉及太多杂七杂八的琐事、千万别进行耗时的操作、尽量不操作一些全局的变量。
  ◇Log的副作用
  不过捏,即使log函数再短小精悍,也还是有可能影响竞态条件(毕竟log也有开销,也要消耗CPU时间)。
  万一竞态条件受到log的影响,那就比较棘手了。我以前就碰到过这种情况:加了log,程序没有问题;去掉log,程序随机崩溃。这种情况一般有两种可能:要么是log功能本身有问题,要么是程序的竞态条件非常敏感(连log的开销都会有影响)。
  这时候你能依靠的就只有肉眼和人脑了。先把相关的代码和文档仔细看上几遍(最好再找其他有经验的人一起Code Review),然后大家一起开动脑筋使劲琢磨。

  ★关于Debug版本和Release版本
  C++程序经常有Debug版本和Release版本的区别。有些时候,这也会导致一些多线程的问题。
  由于Debug版本包含了一些调试信息、启用了某些调试机制(比如assert宏)。所以就可能 影响到多线程的竞争状态。在倒霉的时候,会碰上Debug版本工作正常,Release版本程序随机崩溃。要避免这种情况,可以考虑下面两个办法:
  ◇放弃使用Debug版本
  你可以干脆放弃使用Debug版本。在这种情况下,你需要考虑把诸如assert之类调试相关的宏替换成自己的一套宏,使得在非Debug版本下也可以生效。
  ◇两种版本同步测试
  使用此方法,程序员平时自测可以使用Debug版本,但是测试人员日常测试的必须是Release版本。具体的操作步骤可以利用每日构建来辅助进行(每日构建的介绍参见“这里 ”)。一定要避免:在平时仅仅搞Debug版本的测试,等到发布前夕再制作Release版本。这种做法是非常危险的!

  ★关于测试的机器(硬件)
  说一个亲身经历、印象深刻的事情。
  当年用ACE开发跨平台程序的时候,公司内的的开发环境和测试环境都是单CPU的机器。因为当时多核的机器还没有面世,多CPU的机器又挺贵,公司没舍得花钱配置。
  软件开发完之后,测试人员经过几轮回归测试,也没发现太大问题。但是拿到客户的环境中运行,却经常会随机性崩溃。因为不能在客户环境中Debug,自己的环境又死活没问题,开发组的几个人只好充分发挥肉眼和人脑的功能(盯着代码和设计文档猛想)。经过N长时间,差点把脑袋想破,最后才意识到客户的机器是多CPU的。然后赶紧从其它部门借了一台多CPU机器,装上软件调试,最后查出是一个第三方库有问题。此事过后,我立即想出各种法子,去申请了几台多CPU机器给测试人员用。
  由于上述的前车之鉴,所以我强烈建议:如果是开发多线程的应用程序,尽量给每一个 编程人员和测试人员都配置多核/多CPU的机器。毕竟现在多核机器已经很普及了,即使多CPU的机器,价格也还凑合。实在没必要为了省那点小钱而引入开发风险(不光会浪费开发/测试人员的时间,还可能增加实施和维护的成本)。
  另外,可能有同学会问“超线程的机器如何捏?”关于多CPU 、多核 、超线程 三者之间的差异,有兴趣的同学可以看“这里 ”。我个人感觉超线程不如多核与多CPU爽。


--------------------------------------------------------------------------------

版权声明
本博客所有的原创文章,作者皆保留版权。转载必须包含本声明,保持本文完整,并以超链接形式注明作者编程随想 和本文原始地址:

http://program-think.blogspot.com/2009/04/debug-test-multithreaded-applications.html

分享到:
评论

相关推荐

    zint-2.12.0-静态库(不能输出png图片)

    源码文件:zint-2.12.0-src.tar.gz 编译环境:Win11 64 位 + VS2017 使用环境:理论上适用所有Windows系统 注意事项: 1、编译工程时,添加了宏“ZINT_NO_PNG”,不依赖... 多线程调试DLL(/MDd) → libzint_MDd.lib

    Visual C++开发经验技巧宝典(第1章)

    0001 注释的使用方法及注意事项 2 0002 使用汇编语言 2 0003 如何使用内联函数 2 0004 如何使用#define自定义宏 3 0005 使用goto语句进行无条件跳转 3 0006 while循环转为for循环 3 0007 do while循环...

    Visual C++/Turbo C串口通信编程实践 及源代码-1

    11.4 串口调试注意事项 318 11.5 常用数据校验法 318 11.5.1 奇偶校验 318 11.5.2 循环冗余码校验 319 11.6 串口连接和tcp/ip连接对比 320 11.7 现场总线与rs-232、rs-485的本质区别 320 11.8 modem通信技术 ...

    Visual C++/Turbo C串口通信编程实践及源代码-3

    11.4 串口调试注意事项 318 11.5 常用数据校验法 318 11.5.1 奇偶校验 318 11.5.2 循环冗余码校验 319 11.6 串口连接和tcp/ip连接对比 320 11.7 现场总线与rs-232、rs-485的本质区别 320 11.8 modem通信技术 ...

    Visual_C++精彩实例详解

    注意事项: 1. 若读者将源代码光盘全部复制到硬盘上调试,则复制完成后应该去掉文件的只读属性,否则在硬盘上重新编译工程时会出现不可写的提示。 2. 本书例程大部分采用的是动态链接,读者在未装VC的PC机上运行每章...

    Visual C++_Turbo C 串口通信编程实践.(电子工业.龚建伟.熊光明) 第二版 电子版

    11.4 串口调试注意事项 318 11.5 常用数据校验法 318 11.5.1 奇偶校验 318 11.5.2 循环冗余码校验 319 11.6 串口连接和TCP/IP连接对比 320 11.7 现场总线与RS-232、RS-485的本质区别 320 11.8 MODEM通信技术 320 ...

    visual C++_Turbo C串口通信编程实践

    11.4 串口调试注意事项 11.5 常用数据校验法 11.5.1奇偶校验 11.5.2 循环冗余码校验 11.6 串口连接和TCP/IP连接对比 11.7 现场总线与RS232、RS485的本质区别 11.8 Modem通信技术 11.8.1 Modem的基本...

    Symbian OS C++高效编程

    OS中的类命名约定 11.1 基本类型 11.2 T类 21.3 C类 31.4 R类 51.5 M类 61.6 静态类 91.7 使用者注意事项 91.8 小结 9第2章 异常退出(leave):Symbian OS的异常 102.1 异常退出函数 102.2 ...

    Visual C++/Turbo C串口通信编程实践及源代码-2

    11.4 串口调试注意事项 318 11.5 常用数据校验法 318 11.5.1 奇偶校验 318 11.5.2 循环冗余码校验 319 11.6 串口连接和tcp/ip连接对比 320 11.7 现场总线与rs-232、rs-485的本质区别 320 11.8 modem通信技术 ...

    Visual C++_Turbo C 串口通信编程实践.(电子工业.龚建伟.熊光明) 源码光盘

    11.4 串口调试注意事项 318 11.5 常用数据校验法 318 11.5.1 奇偶校验 318 11.5.2 循环冗余码校验 319 11.6 串口连接和TCP/IP连接对比 320 11.7 现场总线与RS-232、RS-485的本质区别 320 11.8 MODEM通信技术 320 ...

    Visual_C++精彩实例详解.part2.rar

    注意事项: 1. 若读者将源代码光盘全部复制到硬盘上调试,则复制完成后应该去掉文件的只读属性,否则在硬盘上重新编译工程时会出现不可写的提示。 2. 本书例程大部分采用的是动态链接,读者在未装VC的PC机上运行每章...

    Visual_C++精彩实例详解.part4.rar

    注意事项: 1. 若读者将源代码光盘全部复制到硬盘上调试,则复制完成后应该去掉文件的只读属性,否则在硬盘上重新编译工程时会出现不可写的提示。 2. 本书例程大部分采用的是动态链接,读者在未装VC的PC机上运行每章...

    Visual_C++精彩实例详解.part5.rar

    注意事项: 1. 若读者将源代码光盘全部复制到硬盘上调试,则复制完成后应该去掉文件的只读属性,否则在硬盘上重新编译工程时会出现不可写的提示。 2. 本书例程大部分采用的是动态链接,读者在未装VC的PC机上运行每章...

    Visual_C++精彩实例详解.part3.rar

    注意事项: 1. 若读者将源代码光盘全部复制到硬盘上调试,则复制完成后应该去掉文件的只读属性,否则在硬盘上重新编译工程时会出现不可写的提示。 2. 本书例程大部分采用的是动态链接,读者在未装VC的PC机上运行每章...

    VC学习大纲 VC学习讲义

    Windows程序运行原理及程序编写流程,窗口产生过程,句柄原理,消息队列,回调函数,窗口关闭与应用程序退出的工作关系,使用VC++的若干小技巧,stdcall与cdecl调用规范的比较,初学者常犯错误及注意事项。...

    MySQL5.1参考手册官方简体中文版

    2.9. 安装后的设置和测试 2.9.1. Windows下安装后的过程 2.9.2. Unix下安装后的过程 2.9.3. 使初始MySQL账户安全 2.10. 升级MySQL 2.10.1. 从5.0版升级 2.10.2. 升级授权表 2.10.3. 将MySQL数据库拷贝到另一台机器 ...

    MySQL 5.1参考手册

    2.9. 安装后的设置和测试 2.9.1. Windows下安装后的过程 2.9.2. Unix下安装后的过程 2.9.3. 使初始MySQL账户安全 2.10. 升级MySQL 2.10.1. 从5.0版升级 2.10.2. 升级授权表 2.10.3. 将MySQL数据库拷贝到另一台机器 ...

    MySQL 5.1参考手册 (中文版)

    2.9. 安装后的设置和测试 2.9.1. Windows下安装后的过程 2.9.2. Unix下安装后的过程 2.9.3. 使初始MySQL账户安全 2.10. 升级MySQL 2.10.1. 从5.0版升级 2.10.2. 升级授权表 2.10.3. 将MySQL数据库拷贝到另一台机器 ...

    mysql官方中文参考手册

    2.9. 安装后的设置和测试 2.9.1. Windows下安装后的过程 2.9.2. Unix下安装后的过程 2.9.3. 使初始MySQL账户安全 2.10. 升级MySQL 2.10.1. 从5.0版升级 2.10.2. 升级授权表 2.10.3. 将MySQL数据库拷贝到另一台机器 ...

    MYSQL中文手册

    2.9. 安装后的设置和测试 2.9.1. Windows下安装后的过程 2.9.2. Unix下安装后的过程 2.9.3. 使初始MySQL账户安全 2.10. 升级MySQL 2.10.1. 从5.0版升级 2.10.2. 升级授权表 2.10.3. 将MySQL数据库拷贝到另一...

Global site tag (gtag.js) - Google Analytics