C++多线程调试和测试的注意事项
在上次的帖子聊了C++多线程的跨平台问题,后来感觉意犹未尽。今天顺便说一下开发C++多线程应用程序时,有关调试和测试的一些注意事项。下面这些注意事项主要是针对C++,不过有些对于其它的语言也适用。
一、关于设置断点和单步执行
很多同学非常依赖于调试器的断点功能和单步功能。这在单线程情况下倒还好(不过有些单线程但涉及GUI的程序,也会有点麻烦)。至于多线程程序的调试,这两种手段简直就是噩梦的开始。多线程造成的主要问题大都和竞态条件(Race Condition,详细解释看“这里 ”)有关。
而设置断点或单步跟踪可能会严重干扰 多线程之间的竞争状态。导致你看到的是一个假象。比如本来有两个线程并发执行,存在某些不和谐的Bug(由竞态引起)。一旦你在某一个线程设置了断点,该线程在断点处停住了,只剩下另一个线程在跑。这时候,并发的场景已经完全被破坏了,你通过调试器看到的可能 是一个和谐的场景。
稍微跑一下题。这很类似量子力学的“测不准原理”,观测者的观测行为干扰了被测量的客体,导致观测者看到的是一个干扰后的现象。
二、关于Log输出
既然断点和单步不好用。那咋办捏?一个替代方案是输出log日志。它可以有效减轻断点和单步所导致的(针对竞态条件的)副作用。
1、传统Log机制的问题
传统的log输出主要是打印到屏幕或者输出到文件。对于C++而言,标准库内置的类和函数(比如cout、printf、fputs)可能会有线程安全的问题(和编译器的具体实现有关)。尤其是标准流类库(iostream)的八个全局对象,更是要小心慎用。轻则输出的log文本混杂,重则导致程序崩溃。
鉴于上述原因,应该尽量使用第三方线程库内置的log机制来搞定log输出功能。比如ACE内置的ACE_Log_Msg等。
2、Log函数要短小精悍
很多情况下,我们会包装一个公用的函数来实现log输出功能。然后在该函数内部调用线程库的log类/函数。为了不影响线程的竞态条件,这个log函数要尽可能简单轻便:不要涉及太多杂七杂八的琐事、千万别进行耗时的操作、尽量不操作一些全局的变量。
3、Log的副作用
不过捏,即使log函数再短小精悍,也还是有可能影响竞态条件(毕竟log也有开销,也要消耗CPU时间)。
万一竞态条件受到log的影响,那就比较棘手了。我以前就碰到过这种情况:加了log,程序没有问题;去掉log,程序随机崩溃。这种情况一般有两种可能:要么是log功能本身有问题,要么是程序的竞态条件非常敏感(连log的开销都会有影响)。
这时候你能依靠的就只有肉眼和人脑了。先把相关的代码和文档仔细看上几遍(最好再找其他有经验的人一起Code Review),然后大家一起开动脑筋使劲琢磨。
三、关于Debug版本和Release版本
C++程序经常有Debug版本和Release版本的区别。有些时候,这也会导致一些多线程的问题。
由于Debug版本包含了一些调试信息、启用了某些调试机制(比如assert宏)。所以就可能 影响到多线程的竞争状态。在倒霉的时候,会碰上Debug版本工作正常,Release版本程序随机崩溃。要避免这种情况,可以考虑下面两个办法:
1、放弃使用Debug版本
你可以干脆放弃使用Debug版本。在这种情况下,你需要考虑把诸如assert之类调试相关的宏替换成自己的一套宏,使得在非Debug版本下也可以生效。
2、两种版本同步测试
使用此方法,程序员平时自测可以使用Debug版本,但是测试人员日常测试的必须是Release版本。具体的操作步骤可以利用每日构建来辅助进行(每日构建的介绍参见“这里 ”)。一定要避免:在平时仅仅搞Debug版本的测试,等到发布前夕再制作Release版本。这种做法是非常危险的!
四、关于测试的机器(硬件)
说一个亲身经历、印象深刻的事情。
当年用ACE开发跨平台程序的时候,公司内的的开发环境和测试环境都是单CPU的机器。因为当时多核的机器还没有面世,多CPU的机器又挺贵,公司没舍得花钱配置。
软件开发完之后,测试人员经过几轮回归测试,也没发现太大问题。但是拿到客户的环境中运行,却经常会随机性崩溃。因为不能在客户环境中Debug,自己的环境又死活没问题,开发组的几个人只好充分发挥肉眼和人脑的功能(盯着代码和设计文档猛想)。经过N长时间,差点把脑袋想破,最后才意识到客户的机器是多CPU的。然后赶紧从其它部门借了一台多CPU机器,装上软件调试,最后查出是一个第三方库有问题。此事过后,我立即想出各种法子,去申请了几台多CPU机器给测试人员用。
由于上述的前车之鉴,所以我强烈建议:如果是开发多线程的应用程序,尽量给每一个 编程人员和测试人员都配置多核/多CPU的机器。毕竟现在多核机器已经很普及了,即使多CPU的机器,价格也还凑合。实在没必要为了省那点小钱而引入开发风险(不光会浪费开发/测试人员的时间,还可能增加实施和维护的成本)
C++多线程调试和测试的注意事项的更多相关文章
- C++(vs)多线程调试 (转)
在一个程序中,这些独立运行的程序片断叫作“线程”(Thread),利用它编程的概念就叫作“多线程处理”.利用线程,用户可按下一个按钮,然后程序会立即作出响应,而不是让用户等待程序完成了当前任务以后 ...
- GDB多线程调试
一.多线程调试1. 多线程调试,最重要的几个命令:info threads 查看当前进程的线程. ...
- 利用GDB进行多线程调试
一.多线程调试 多线程调试重要就是下面几个命令: info thread 查看当前进程的线程. thread <ID> 切换调试的线程为指定ID的线程. break file.c:100 ...
- GDB 多线程调试:只停止断点的线程,其他线程任然执行; 或只运行某些线程 其他线程中断
多线程调试之痛 调试器(如VS2008和老版GDB)往往只支持all-stop模式,调试多线程程序时,如果某个线程断在一个断点上,你的调试器会让整个程序freeze,直到你continue这个线程,程 ...
- RabbitMQ调试与测试工具-v1.0.1 -提供下载测试与使用
最近几天在看RabbitMQ,所以发了两天时间写了一个调试和测试工具.方便使用. 下载地址:RabbitMQTool-V1.0.1.zip
- [skill][gdb] gdb 多线程调试
中文快速入门: http://coolshell.cn/articles/3643.html (关于多线程的部署说的并不太对) 进阶: 多进程相关概念: inferiors 是什么? http://m ...
- gdb 多线程调试
gdb 多线程调试 http://hi.baidu.com/hcq11/blog/item/9f5bfc6e696209d680cb4a25.html http://hi.baidu.com/lit ...
- gdb的多线程调试
info threads 可以查看当前进程有哪些线程 thread ID 可以切换到线程ID bt 查看当前线程堆栈 set scheduler-locking on多线程调试过程中, 线程会来回切换 ...
- python多线程在渗透测试中的应用
难易程度:★★★ 阅读点:python;web安全; 文章作者:xiaoye 文章来源:i春秋 关键字:网络渗透技术 前言 python是门简单易学的语言,强大的第三方库让我们在编程中事半功倍,今天, ...
随机推荐
- pro5
1.本次课学到的知识点 (1)循环结构的概念 在我们需要重复进行某个步骤是就需要运用到循环结构. (2)三种循环语句 for,while,do-while是三种常用的循环语句,其中while的适用范围 ...
- 【nodejs】使用Node.js实现REST Client调用REST API
最近在产品中开发基于REST的API接口,结合自己最近对Node.js的研究,想基于它开发一个REST Client做测试之用. 通过初步研究,Node.js开发HTTP Client还是挺方便的. ...
- Android高级之第十一讲Hybird开发
本文来自http://blog.csdn.net/liuxian13183/ ,引用必须注明出处! 随着移动端应用平台的兴起,需求和交互方式的多样化,H5开发逐渐在移动端流行起来:常见的移动产品有We ...
- Silverlight页面通过继承扩展实现
在Silverlight中有些项目对UserControl重新做了封装基类,如PageBase,要求项目中每个页面都要从PageBase派生,但是过程比较坎坷,本文针对这个功能点的实现以及实现过程中遇 ...
- 判断webkit中的js引擎是否是v8
<html><head></head><body><script type="text/javascript"> if ...
- Appium-001-测试开发环境搭建(Android - Win7)
随着移动端 App 测试自动化的兴起,为更好的控制产品质量,越来越多的中大型公司开始了移动端的自动化测试.Appium 自动化测试技术也是我很早之前就想学习的一门技术,却一直没有比较空余的时间来学习( ...
- JSON.stringify实例应用—将对象转换成JSON类型进行AJAX异步传值
在上一篇中,对JSON.stringify()方法有了初步的认识,并且做了一些简单的例子.本篇将进一步将JSON.stringify用在复杂些的实例中,例如如下需求: 在进jQuery AJAX异步传 ...
- 关于action script与js相互调用的Security Error问题
大家都知道,as和js相互调用可以通过ExternalInterface.call和ExternalInterface.addCallback来进行. 比较好的做法是使用之前通过ExternalInt ...
- Java 实现网站当前在线用户统计
1. import java.util.HashSet; import javax.servlet.ServletContext; import javax.servlet.http.HttpSess ...
- @SuppressWarnings
http://www.cnblogs.com/fsjohnhuang/p/4040785.html 一.前言 编码时我们总会发现如下变量未被使用的警告提示: 上述代码编译通过且可以运行,但每行前面的“ ...