到目前为止,我已经避免了测试多线程代码的噩梦,因为它似乎是一个太大的雷区。我想知道人们是如何测试依赖于线程的代码以获得成功执行的,或者人们是如何测试那些仅在两个线程以给定方式交互时才会出现的问题的?

对于今天的程序员来说,这似乎是一个非常关键的问题,恕我直言,将我们的知识集中在这个问题上是很有用的。


当前回答

上周我花了大部分时间在大学图书馆学习并发代码的调试。核心问题是并发代码是不确定的。通常,学术调试可以分为三个阵营之一:

Event-trace/replay. This requires an event monitor and then reviewing the events that were sent. In a UT framework, this would involve manually sending the events as part of a test, and then doing post-mortem reviews. Scriptable. This is where you interact with the running code with a set of triggers. "On x > foo, baz()". This could be interpreted into a UT framework where you have a run-time system triggering a given test on a certain condition. Interactive. This obviously won't work in an automatic testing situation. ;)

现在,正如上面评论者所注意到的,您可以将并发系统设计成更确定的状态。然而,如果你做得不好,你就又回到了设计顺序系统的问题上。

我的建议是,专注于制定一个非常严格的设计协议,规定什么是线程,什么不是线程。如果你限制了你的接口,使元素之间的依赖最小化,那就容易多了。

祝你好运,继续解决这个问题。

其他回答

等待在帮助您编写确定性单元测试时也很有用。它允许您等待系统中的某个状态更新。例如:

await().untilCall( to(myService).myMethod(), greaterThan(3) );

or

await().atMost(5,SECONDS).until(fieldIn(myObject).ofType(int.class), equalTo(1));

它还支持Scala和Groovy。

await until { something() > 4 } // Scala example

Pete Goodliffe有一个关于线程代码单元测试的系列。

是很困难的。我采用了更简单的方法,尽量将线程代码从实际测试中抽象出来。皮特确实提到了我分手的方式是错误的但我要么是正确的,要么就是我很幸运。

For J2E code, I've used SilkPerformer, LoadRunner and JMeter for concurrency testing of threads. They all do the same thing. Basically, they give you a relatively simple interface for administrating their version of the proxy server, required, in order to analyze the TCP/IP data stream, and simulate multiple users making simultaneous requests to your app server. The proxy server can give you the ability to do things like analyze the requests made, by presenting the whole page and URL sent to the server, as well as the response from the server, after processing the request.

您可以在不安全的http模式下找到一些错误,在这种模式下,您至少可以分析正在发送的表单数据,并为每个用户系统地更改表单数据。但真正的测试是在https(安全套接字层)中运行。然后,您还必须有系统地修改会话和cookie数据,这可能有点复杂。

在测试并发性时,我发现的最好的错误是,当我发现开发人员在登录时依赖Java垃圾收集来关闭登录时建立的到LDAP服务器的连接请求。这导致用户暴露在其他用户的会话中,当试图分析服务器瘫痪时发生了什么,几乎每隔几秒钟就能完成一次事务时,结果非常令人困惑。

In the end, you or someone will probably have to buckle down and analyze the code for blunders like the one I just mentioned. And an open discussion across departments, like the one that occurred, when we unfolded the problem described above, are most useful. But these tools are the best solution to testing multi-threaded code. JMeter is open source. SilkPerformer and LoadRunner are proprietary. If you really want to know whether your app is thread safe, that's how the big boys do it. I've done this for very large companies professionally, so I'm not guessing. I'm speaking from personal experience.

提醒一句:理解这些工具确实需要一些时间。这不是简单地安装软件并启动GUI的问题,除非您已经接触过多线程编程。我试图确定需要理解的3个关键领域(表单、会话和cookie数据),希望至少从理解这些主题开始,可以帮助您集中精力快速获得结果,而不必通读整个文档。

听着,要做到这一点并不容易。我正在做一个本来就是多线程的项目。事件来自操作系统,我必须并发地处理它们。

处理测试复杂的多线程应用程序代码的最简单方法是:如果它太复杂而无法测试,那么您做错了。如果您有一个单独的实例,其中有多个线程作用于它,并且您无法测试这些线程相互踩在一起的情况,那么您的设计需要重做。它既简单又复杂。

有许多方法可以为多线程编程,以避免线程同时通过实例运行。最简单的方法是使所有对象都是不可变的。当然,这通常是不可能的。因此,您必须在设计中确定线程与同一实例交互的地方,并减少这些地方的数量。通过这样做,您可以隔离多线程实际发生的几个类,从而降低测试系统的总体复杂性。

但是您必须意识到,即使这样做,您仍然不能测试两个线程相互践踏的每一种情况。要做到这一点,您必须在同一个测试中并发地运行两个线程,然后准确地控制它们在任何给定时刻执行的行。你能做的就是模拟这种情况。但这可能需要您专门为测试编写代码,这充其量是迈向真正解决方案的半步。

测试代码是否存在线程问题的最好方法可能是对代码进行静态分析。如果您的线程代码没有遵循有限的线程安全模式集,那么您可能会遇到问题。我相信VS中的代码分析确实包含了一些线程的知识,但可能不多。

看,就目前的情况来看(可能还会持续很长一段时间),测试多线程应用程序的最佳方法是尽可能降低线程代码的复杂性。最小化线程交互的区域,尽可能地进行测试,并使用代码分析来识别危险区域。

看看我的相关答案在

为自定义Barrier设计一个Test类

它偏向于Java,但对选项进行了合理的总结。

总而言之(我认为),它不是使用一些花哨的框架来确保正确性,而是如何设计你的多线程代码。拆分关注点(并发性和功能性)有助于提高信心。测试引导的面向对象软件的发展比我能更好地解释一些选项。

静态分析和形式化方法(参见并发性:状态模型和Java程序)是一种选择,但我发现它们在商业开发中用处有限。

不要忘记,任何加载/浸泡风格的测试都很少能保证突出问题。

好运!