在一个程序中,这些独立运行的程序片断叫作“线程”(Thread),利用它编程的概念就叫作“多线程处理”。利用线程,用户可按下一个按钮,然后程序会立即作出响应,而不是让用户等待程序完成了当前任务以后才开始响应。

在上次的帖子聊了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++多线程调试和测试的注意事项的更多相关文章

  1. C++(vs)多线程调试 (转)

      在一个程序中,这些独立运行的程序片断叫作“线程”(Thread),利用它编程的概念就叫作“多线程处理”.利用线程,用户可按下一个按钮,然后程序会立即作出响应,而不是让用户等待程序完成了当前任务以后 ...

  2. GDB多线程调试

    一.多线程调试1. 多线程调试,最重要的几个命令:info threads                        查看当前进程的线程.                              ...

  3. 利用GDB进行多线程调试

    一.多线程调试 多线程调试重要就是下面几个命令: info thread 查看当前进程的线程. thread <ID> 切换调试的线程为指定ID的线程. break file.c:100 ...

  4. GDB 多线程调试:只停止断点的线程,其他线程任然执行; 或只运行某些线程 其他线程中断

    多线程调试之痛 调试器(如VS2008和老版GDB)往往只支持all-stop模式,调试多线程程序时,如果某个线程断在一个断点上,你的调试器会让整个程序freeze,直到你continue这个线程,程 ...

  5. RabbitMQ调试与测试工具-v1.0.1 -提供下载测试与使用

    最近几天在看RabbitMQ,所以发了两天时间写了一个调试和测试工具.方便使用. 下载地址:RabbitMQTool-V1.0.1.zip

  6. [skill][gdb] gdb 多线程调试

    中文快速入门: http://coolshell.cn/articles/3643.html (关于多线程的部署说的并不太对) 进阶: 多进程相关概念: inferiors 是什么? http://m ...

  7. gdb 多线程调试

    gdb 多线程调试 http://hi.baidu.com/hcq11/blog/item/9f5bfc6e696209d680cb4a25.html  http://hi.baidu.com/lit ...

  8. gdb的多线程调试

    info threads 可以查看当前进程有哪些线程 thread ID 可以切换到线程ID bt 查看当前线程堆栈 set scheduler-locking on多线程调试过程中, 线程会来回切换 ...

  9. python多线程在渗透测试中的应用

    难易程度:★★★ 阅读点:python;web安全; 文章作者:xiaoye 文章来源:i春秋 关键字:网络渗透技术 前言 python是门简单易学的语言,强大的第三方库让我们在编程中事半功倍,今天, ...

随机推荐

  1. 关于Hive的调优(本身,sql,mapreduce)

    1.关于hive的优化 ->大表拆分小表 ->过滤字段 ->按字段分类存放 ->外部表与分区表 ->外部表:删除时只删除元数据信息,不删除数据文件 多人使用多个外部表操作 ...

  2. zepto源码--extend--学习笔记

    对象继承函数: $.extend(){},默认传递一个参数,需要继承的对象目标.函数展示: 最终实现的过程,需要调用工具函数extend,首先分析extend函数. 默认传递三个参数,继承的目标对象- ...

  3. PHP实现QQ第三方登录

    PHP实现QQ第三方登录 学习之前,请大家先看一下oAuth协议. 首先呢,我们进入QQ互联的官方网站 http://connect.qq.com登入我们自己的QQ号,没有QQ号的小伙伴可以忽略本篇博 ...

  4. JQuery源码之“对象的结构解析”

    吃完午饭,觉得有点发困,想起了以后我们的产品可能要做到各种浏览器的兼容于是乎不得不清醒起来!我们的web项目多数是依赖于Jquery的.据了解,在Jquery的2.0版本以后对IE的低端版本浏览器不再 ...

  5. new char[]和new char()的区别

    new char[1];分配一块连续的内存,也就是一个数组,里面有1个元素new   char(1);分配一块内存,有一个元素,该元素被初始化为1

  6. android游戏动画特效的一些处理

    游戏中避免不了需要一些动画特效的处理,有些是不方便用美术或者美工来处理的,那么就由我们程序猿来搞了.直接进入正题. 首先是Animation,Animation针对view,可以控制view的位移.缩 ...

  7. asp.net mvc页面javascript代码中如何使用razor

    我们需要用<text>将javascript代码包含起来,强制让razor编译器回到内容模式, 或者将javascript代码放在函数中,让razor编译器可以识别,请看下面两个例子: & ...

  8. LightOj1383 - Underwater Snipers(贪心 + 二分)

    题目链接:http://lightoj.com/volume_showproblem.php?problem=1383 题意:在平面图中,有一条河,用直线y=k表示,河上面(y>k)的都是敌方区 ...

  9. MFC之目录结构及消息流转(一)

    跟上时代,用vs2010, 新建一个MFC应用程序Helloworld. 目录结构: 所有文件分为6个部分:解决方案相关文件.工程相关文件.应用程序头文件和源文件.资源文件.预编译头文件和编译链接生成 ...

  10. 【转】Android新组件Material Dialog,SwipeRefreshLayout,ListPopupWindow,PopupMenu等

    朝花夕拾----新组件的学习和使用 分类: Android UI2015-06-26 11:31 440人阅读 评论(0) 收藏 举报 uidialogMaterial   目录(?)[-] Mate ...