多线程软件的测试方法

Sma*_*acL 11 c++ testing mfc multithreading

我有一块成熟的地理空间软件,最近重新编写了一些区域,以便更好地利用现代PC中的多处理器.具体来说,显示,GUI,空间搜索和主要处理都被分离出来以分离线程.该软件具有相当大的GUI自动化套件,用于功能回归,另一个较小的用于性能回归.虽然所有自动化测试都已通过,但我并不相信它们能够提供足够的覆盖范围来查找与多线程相关的竞争条件,死锁和其他恶意相关的错误.您将使用哪些技术来查看是否存在此类错误?假设有一些技术可以根除,那么你会提倡哪些技术可以根除它们?

我到目前为止所做的是在调试器下运行的应用程序上运行GUI功能自动化,这样我就可以摆脱死锁并捕获崩溃,并计划进行边界检查器构建并针对该版本重复测试.我还通过PC-Lint对源进行了静态分析,希望找到潜在的死锁,但没有任何有价值的结果.

该应用程序是C++,MFC,多文档/视图,每个文档有许多线程.我使用的锁定机构是基于一个对象,它包括一个指针指向一个CMutex,其被锁定在构造函数和在析构函数释放上.我使用此对象的局部变量来根据需要锁定各种代码,并且我的互斥锁有一个超时,如果达到超时,则会触发警告.尽可能使用资源副本,尽可能避免锁定.

您还会进行哪些其他测试?

编辑:我已经在许多不同的测试和编程论坛上发布了这个问题,因为我很想知道不同的思维方式和思想流派如何解决这个问题.如果你看到它在其他地方交叉发布,那么道歉.我会在一周左右后提供回复的摘要链接

oef*_*efe 10

一些建议:

  • 利用大数定律不仅可以执行一次,而且可以多次执行被测操作.
  • 通过夸大场景来对代码进行压力测试.例如,要测试你的互斥锁类,请使用受互斥锁保护的代码:
    • 非常短而快(单指令)
    • 很耗时(睡眠价值很大)
    • 包含显式上下文切换(Sleep(0))
  • 在各种不同的架构上运行测试.(即使您的软件仅限Windows,也可以在具有和不具有超线程的单核和多核处理器上进行测试,以及各种时钟速度)
  • 尝试设计您的代码,使其大多数不会暴露于多线程问题.例如,不要访问共享数据(需要锁定或非常精心设计的锁定避免技术),而是让工作线程对数据副本进行操作,并使用队列与它们进行通信.然后,您只需要测试队列类的线程安全性
  • 在系统空闲时以及从其他任务加载时运行测试(例如,我们的构建服务器经常并行运行多个构建.仅此一项就显示了系统负载时发生的许多多线程错误.)
  • 避免在超时时断言.如果这样的断言失败,您不知道代码是否被破坏或者超时是否太短.相反,使用非常慷慨的超时(只是为了确保测试最终失败).如果要测试操作的持续时间不会超过一定时间,请测量持续时间,但不要使用超时.


Len*_*ate 7

虽然我同意@rstevens的答案,因为目前没有办法100%确定单元测试线程问题,但我发现有些东西是有用的.

首先,无论你做什么测试,都要确保在很多不同的规格盒上运行它们.我有几个构建机器,所有不同的,多核的,单核的,快速的,慢的等等.它们有多么多样化的好处是不同的机器会引发不同的线程问题.我经常惊讶地在我的农场添加一台新的构建机器并突然暴露出一个新的线程错误; 而我正在谈论在其他构建机器上运行10000次的代码中暴露出来的新bug,并且在新的机器上显示了10个中的1个...

其次,您对代码进行的大多数单元测试都不需要涉及线程.通常,线程是正交的.因此,第一步是将代码分开,以便您可以测试执行工作的实际代码,而不必过多担心线程性质.这通常意味着创建一个线程代码用来驱动实际代码的接口.然后,您可以单独测试实际代码.

通过Thridly,您可以测试线程代码与代码主体交互的位置.这意味着为您开发的接口编写模拟以分隔两个代码块.到目前为止,线程代码可能更简单,然后您可以经常将同步对象放在您已经创建的模拟中,以便您可以控制被测试的代码.因此,您可以启动线程并等待它通过调用模拟来设置事件,然后让它阻止您的测试代码控制的另一个事件.然后,测试代码可以将线程代码从接口中的一个点移动到下一个点.

最后(如果你已经解开了足够的东西,你可以做更早的事情然后这很容易)你可以运行测试中的应用程序的多线程部分的大部分,并确保你得到你期望的结果; 你可以使用线程的优先级,甚至可以添加几个测试线程,只需吃CPU就可以搞砸了.

现在,您可以在不同的硬件上多次运行所有这些测试......

我还发现在DevPartner BoundsChecker之类的东西下运行测试(或应用程序)可以帮助很多,因为它与线程调度混淆,因此它有时很难找到bug.我还写了一个死锁检测工具,它在程序执行期间检查锁定反转,但我很少使用它.

您可以在此处查看我如何测试多线程C++代码的示例:http: //www.lenholgate.com/blog/2004/05/practical-testing.html


Sma*_*acL 1

首先,非常感谢您的回复。有关在不同论坛上发布的回复,请参阅;

http://www.sqaforums.com/showflat.php?Cat=0&Number=617621&an=0&page=0#Post617621

多线程软件的测试方法

http://www.softwaretestingclub.com/forum/topics/testing-approach-for?xg_source=activity

以及以下邮件列表;软件测试@yahoogroups.com

测试花费的时间比预期要长得多,因此这个迟来的答复使我得出结论,即使编码非常简单,向现有应用程序添加多线程在测试方面可能会非常昂贵。这对于 SQA 社区来说可能很有趣,因为那里正在进行越来越多的多线程开发。

根据 Joe Strazzere 的建议,我发现解决错误的最有效方法是通过具有不同输入的自动化。我最终在三台电脑上完成了这项工作,这三台电脑在大约六周的时间里用不同的输入反复运行了一系列测试。最初,我发现每台电脑每天都会崩溃一两次。当我追踪这些问题时,最终发现三台电脑之间每周都会出现一到两个问题,并且在过去两周内我们没有遇到任何进一步的问题。在过去的两周里,我们还对用户进行了 beta 测试,并且正在内部使用该软件。

除了在自动化下改变输入之外,我还从以下方面获得了良好的结果;

  • 添加一个测试选项,允许从配置文件读取互斥超时,而这又可以由我的自动化控制。

  • 将互斥体超时延长到执行线程代码部分预期的典型时间之外,并在超时时引发调试异常。

  • 与调试器 (VS2008) 结合运行自动化,以便在出现问题时有更好的机会进行跟踪。

  • 在不使用调试器的情况下运行,以确保调试器不会隐藏其他与时序相关的错误。

  • 针对正常发布、调试和完全优化的构建运行自动化。FWIW,优化的构建引发了其他构建中无法重现的错误。

发现的错误类型本质上往往很严重,例如取消引用无效指针,甚至在调试器下也需要进行大量跟踪。正如其他地方所讨论的,SuspendThread 和 ResumeThread 函数最终成为罪魁祸首,并且这些函数的所有使用都被互斥体所取代。同样,由于缺乏超时,所有关键部分都被删除。关闭文档和退出程序也是一个错误来源,在一个实例中,文档被破坏,而工作线程仍然处于活动状态。为了克服这个问题,每个线程添加了一个互斥体来控制线程的生命周期,并由文档析构函数获取以确保线程按预期终止。

再次非常感谢您提供的所有详细且多样的答复。下次我参加此类活动时,我会做好更好的准备。