使用TDD驱逐线程安全的代码

时间:2009-02-11 14:27:39

标签: multithreading tdd

利用TDD驱逐线程安全代码的好方法是什么?例如,假设我有一个工厂方法,它使用延迟初始化来创建一个类的一个实例,然后返回它:

private TextLineEncoder textLineEncoder;
...
public ProtocolEncoder getEncoder() throws Exception {
    if(textLineEncoder == null)
        textLineEncoder = new TextLineEncoder();
    return textLineEncoder;
}

现在,我想以良好的TDD方式编写测试,迫使我使这段代码成为线程安全的。具体来说,当两个线程同时调用此方法时,我不想创建两个实例并丢弃一个实例。这很容易做到,但我怎样才能编写让我这样做的测试?

我在Java中问这个问题,但答案应该更广泛适用。

6 个答案:

答案 0 :(得分:5)

你可以注入一个“提供者”(一个非常简单的工厂),它只负责这一行:

 textLineEncoder = new TextLineEncoder();

然后你的测试会注入一个非常慢的提供者实现。这样,测试中的两个线程可能更容易发生碰撞。你可以在第一个线程上等待第二个线程释放的信号量。然后测试成功将确保等待线程超时。通过给第一个线程启动,您可以确保它在第二个线程释放之前等待。

答案 1 :(得分:3)

虽然可能很难 - 可能比它的价值更难。已知的解决方案涉及检测被测代码。讨论here, "Extreme Programming Challenge Fourteen"值得筛选。

答案 2 :(得分:2)

在书Clean Code中,有一些关于如何测试并发代码的技巧。帮助我找到并发错误的一个技巧是同时运行比CPU有核心更多的测试。

my project中,在我的四核机器上运行测试大约需要2秒钟。当我想测试并发部分(有一些测试)时,我在IntelliJ IDEA中按下运行所有​​测试的热键,直到我在状态栏中看到正在执行20,50或100次测试运行。我在Windows任务管理器中跟踪CPU和内存使用情况,以了解所有测试运行何时完成执行(内存使用量在运行时会增加1-2 GB,然后慢慢降低)。

然后我逐个关闭所有测试运行输出对话框,并检查没有失败。有时会出现失败的测试或测试失败,然后我会对它们进行调查,直到找到错误并修复它。这有助于我找到一些令人讨厌的并发错误。最重要的是,当遇到不应该发生的异常/死锁时,总是假设代码被破坏,并且无情地调查原因并修复它。没有宇宙射线导致程序随机崩溃 - 代码中的错误导致程序崩溃。

还有一些框架,例如http://www.alphaworks.ibm.com/tech/contest,它使用字节码操作来强制代码进行更多的线程切换,从而增加了使并发错误可见的可能性。

答案 3 :(得分:2)

当我测试驱动最近需要线程安全的实现时,我想出了我提供的解决方案作为this question的答案。希望即使没有测试也有帮助。希望链接比重复回答更好......

答案 4 :(得分:1)

Java Concurrency in Practice的第12章称为“测试并发程序”。它记录了安全性和活力测试,但表示这是一个很难的主题。我不确定这一章的工具是否可以解决这个问题。

答案 5 :(得分:0)

在我的脑海中,您可以比较返回的实例,看看它们是否确实是同一个实例,或者它们是否不同?这可能就是我从C#开始的地方,我想你可以在java中做同样的事情