到目前为止,我已经避免了测试多线程代码的噩梦,因为它似乎是一个太大的雷区。我想知道人们是如何测试依赖于线程的代码以获得成功执行的,或者人们是如何测试那些仅在两个线程以给定方式交互时才会出现的问题的?
对于今天的程序员来说,这似乎是一个非常关键的问题,恕我直言,将我们的知识集中在这个问题上是很有用的。
到目前为止,我已经避免了测试多线程代码的噩梦,因为它似乎是一个太大的雷区。我想知道人们是如何测试依赖于线程的代码以获得成功执行的,或者人们是如何测试那些仅在两个线程以给定方式交互时才会出现的问题的?
对于今天的程序员来说,这似乎是一个非常关键的问题,恕我直言,将我们的知识集中在这个问题上是很有用的。
当前回答
我用与处理任何单元测试相同的方式处理线程组件的单元测试,即使用反转控制和隔离框架。我在. net领域进行开发,开箱即用的线程(以及其他东西)很难(我可以说几乎不可能)完全隔离。
因此,我写的包装器看起来像这样(简化):
public interface IThread
{
void Start();
...
}
public class ThreadWrapper : IThread
{
private readonly Thread _thread;
public ThreadWrapper(ThreadStart threadStart)
{
_thread = new Thread(threadStart);
}
public Start()
{
_thread.Start();
}
}
public interface IThreadingManager
{
IThread CreateThread(ThreadStart threadStart);
}
public class ThreadingManager : IThreadingManager
{
public IThread CreateThread(ThreadStart threadStart)
{
return new ThreadWrapper(threadStart)
}
}
从那里,我可以很容易地将IThreadingManager注入到组件中,并使用所选的隔离框架使线程在测试期间的行为符合我的预期。
到目前为止,这对我来说工作得很好,我对线程池,系统中的东西使用相同的方法。环境,睡眠等等。
其他回答
我最近发现了一个叫做Threadsafe的工具(用于Java)。它是一个静态分析工具,很像findbugs,但专门用于发现多线程问题。它不是测试的替代品,但我可以推荐它作为编写可靠的多线程Java的一部分。
它甚至可以捕捉到一些非常微妙的潜在问题,比如类包容、通过并发类访问不安全的对象以及在使用双重检查锁定范式时发现丢失的volatile修饰符。
如果您编写多线程Java,请尝试一下。
我曾经有过测试线程代码的不幸任务,这绝对是我写过的最难的测试。
在编写测试时,我使用委托和事件的组合。基本上,它都是关于使用PropertyNotifyChanged事件和WaitCallback或某种轮询的ConditionalWaiter。
我不确定这是否是最好的方法,但它对我来说是有效的。
您可以使用EasyMock。使测试实例线程安全
一个简单的测试模式可以用于一些(不是所有!)用例,就是多次重复相同的测试。例如,假设你有一个方法:
def process(input):
# Spawns several threads to do the job
# ...
return output
创建一堆测试:
process(input1) -> expect to return output1
process(input2) -> expect to return output2
...
现在将每个测试运行多次。
如果流程的实现包含一个微小的错误(例如死锁、竞态条件等),出现的概率为0.1%,那么运行1000次测试,则该错误至少出现一次的概率为64%。运行测试10000次,得到>99%的概率。
确实很难!在我的(c++)单元测试中,我按照使用的并发模式将其分解为几个类别:
Unit tests for classes that operate in a single thread and aren't thread aware -- easy, test as usual. Unit tests for Monitor objects (those that execute synchronized methods in the callers' thread of control) that expose a synchronized public API -- instantiate multiple mock threads that exercise the API. Construct scenarios that exercise internal conditions of the passive object. Include one longer running test that basically beats the heck out of it from multiple threads for a long period of time. This is unscientific I know but it does build confidence. Unit tests for Active objects (those that encapsulate their own thread or threads of control) -- similar to #2 above with variations depending on the class design. Public API may be blocking or non-blocking, callers may obtain futures, data may arrive at queues or need to be dequeued. There are many combinations possible here; white box away. Still requires multiple mock threads to make calls to the object under test.
题外话:
在我所做的内部开发人员培训中,我教授了并发的支柱和这两种模式,作为思考和分解并发问题的主要框架。显然还有更先进的概念,但我发现这组基础知识可以帮助工程师摆脱困境。正如上面所描述的,它还会导致代码更具单元可测试性。