2009-02-11 6 views
10

Was ist eine gute Möglichkeit, TDD zu nutzen, um Thread-sicheren Code zu entfernen? Zum Beispiel, sagen, dass ich eine Factory-Methode haben, die faul Initialisierung nur eine Instanz einer Klasse erstellen verwendet, und senden Sie es danach:Verwenden von TDD zum Entfernen von threadsicherem Code

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

Nun möchte ich einen Test in einem guten TDD Mode schreiben, die mich zwingt, Machen Sie diesen Code threadsicher. Insbesondere, wenn zwei Threads diese Methode gleichzeitig aufrufen, möchte ich nicht zwei Instanzen erstellen und eine löschen. Das ist leicht gemacht, aber wie kann ich einen Test schreiben, der mich dazu bringt?

Ich frage das in Java, aber die Antwort sollte breiter anwendbar sein.

+0

Danke. Es ist heute Morgen aufgetaucht, als ich gerade eine solche Fabrik geschrieben habe. Es ist schon früher bei der Diskussion des doppelt überprüften Schließmechanismus vorgekommen, aber das war bevor SO da war. –

Antwort

3

Es ist schwer, obwohl möglich - möglicherweise härter als es wert ist. Bekannte Lösungen beinhalten das Instrumentieren des zu testenden Codes. Die Diskussion here, "Extreme Programming Challenge Fourteen" lohnt sich zu durchforsten.

+0

Was für eine faszinierende Idee! Und vielleicht ein Aspekt, der fehlschlägt, wenn TextLineEncoder() ein zweites Mal aufgerufen wird. Hmmm ... –

0

Direkt von der Spitze meines Kopfes können Sie die zurückgegebenen Instanzen vergleichen, um zu sehen, ob sie tatsächlich die gleiche Instanz sind oder ob sie unterschiedlich sind? Das ist wahrscheinlich, wo ich mit C# anfangen würde, würde ich mir vorstellen, dass Sie das gleiche in Java tun können

+0

Klingt wie ein nützlicher Start. Es ist einfach, einen Test dieser Art zu schreiben, jedoch schwieriger, ihn so zu schreiben, dass er aufgrund fehlender Thread-Synchronisation fehlschlägt. – Morendil

+0

Ich denke, es ist ein guter Anfang - aber wenn ein Thread-Wechsel zwischen der Konstruktion und der Rückkehr auftritt, könnten sie zwei Instanzen erstellen, und immer noch beide die gleiche Instanz zurückgeben. –

+0

das ist wahr. Sie müssten sicherstellen, dass Sie um das getestete System herum geeignete Sperren haben. –

1

Kapitel 12 von Java Concurrency in Practice heißt "Testen von Concurrent-Programmen". Es dokumentiert Tests für Sicherheit und Lebendigkeit, sagt aber, dass dies ein hartes Thema ist. Ich bin mir nicht sicher, ob dieses Problem durch die Werkzeuge dieses Kapitels lösbar ist.

5

Sie könnten einen „Provider“ (eine wirklich einfache Fabrik) injizieren, die nur für diese Linie verantwortlich ist:

textLineEncoder = new TextLineEncoder(); 

Dann würde dein Test eine wirklich langsame Umsetzung des Anbieters injizieren. Auf diese Weise könnten die beiden Threads im Test leichter kollidieren. Du könntest so weit gehen, dass der erste Thread auf einen Semaphor wartet, der vom zweiten Thread freigegeben wird. Dann würde der Erfolg des Tests sicherstellen, dass der wartende Thread das Zeitlimit überschreitet. Indem Sie dem ersten Thread einen Vorsprung geben, können Sie sicherstellen, dass er wartet, bevor der zweite veröffentlicht wird.

+1

+1 Ich mag das. Ein wirklich langsamer TextLineEncoder-Konstruktor würde praktisch garantieren, dass das Problem der Parallelität hier auftritt. Eine ITextLineEncoderProvider-Implementierung könnte dies leicht simulieren. –

+0

Und als Bonus können Sie Flexibilität und Erweiterbarkeit (kann verschiedene Textzeilen-Encoder verwenden) und gezielter testen (kann TextLineEncoder Mock verwenden). –

+1

Gute Idee ... Und es kann sogar implementiert werden, ohne eine langsame Fabrik für 'TextLineEncoder' zu verwenden, indem ein Spottwerkzeug verwendet wird, das das Spotten von Konstruktoren unterstützt: im ersten Aufruf an den Konstruktor (erster Thread) würde es einfach blockieren ; Wenn dann der zweite Thread kommt und den Konstruktor ein zweites Mal ausführt, würde der Test fehlschlagen. –

2

In dem Buch Clean Code gibt es einige Tipps zum Testen von gleichzeitigen Code. Ein Tipp, der mir geholfen hat, Nebenläufigkeitsfehler zu finden, führt gleichzeitig mehr Tests durch, als die CPU Kerne hat.

In my project dauert das Ausführen der Tests etwa 2 Sekunden auf meiner Quad-Core-Maschine. Wenn ich die konkurrierenden Teile testen will (es gibt einige Tests dafür), halte ich in IntelliJ IDEA den Hotkey zum Ausführen aller Tests gedrückt, bis ich in der Statusleiste sehe, dass 20, 50 oder 100 Testläufe ausgeführt werden. Ich folge im Windows Task Manager der CPU- und Speicherauslastung, um herauszufinden, wann alle Testläufe beendet sind (die Speicherbelegung steigt um 1-2 GB, wenn alle laufen und geht dann langsam wieder runter).

Dann schließe ich nacheinander alle Testlauf-Ausgabedialoge und überprüfe, dass keine Fehler aufgetreten sind. Manchmal gibt es fehlgeschlagene Tests oder Tests, die sich im Deadlock befinden, und dann untersuche ich sie, bis ich den Fehler gefunden und behoben habe. Das hat mir geholfen, ein paar böse Nebenläufigkeitsfehler zu finden. Die wichtigste Sache, wenn es zu einer Ausnahme/einem Deadlock kommt, die nicht hätte passieren dürfen, ist immer die Annahme, dass der Code kaputt ist und der Grund rücksichtslos untersucht und behoben wird. Es gibt keine kosmischen Strahlen, die dazu führen, dass Programme zufällig abstürzen - Bugs im Code führen zum Absturz von Programmen.

Es gibt auch Frameworks wie http://www.alphaworks.ibm.com/tech/contest, die Bytecode-Manipulation verwenden, um den Code zu zwingen, mehr Threadwechsel durchzuführen, wodurch die Wahrscheinlichkeit erhöht wird, dass Nebenläufigkeitsfehler sichtbar werden.

2

Als ich eine Implementierung getestet habe, die threadsicher sein musste, kam ich vor kurzem auf die Lösung, die ich als Antwort für this question zur Verfügung stellte. Hoffe das hilft auch wenn es dort keine Tests gibt. Hoffnung Verbindung ist OK raher als das Duplizieren der Antwort ...

+0

Ja, ein Link ist in Ordnung. Ich mag deine Antwort wirklich - es ist die erste, die ich gehört habe, die ein Gefühl der Richtigkeit darüber hat. Vielleicht, weil viele andere sich auf nicht-deterministisches Verhalten verlassen. Besonders überzeugend ist Ihr Kommentar: "Zuallererst hat Ihre bestehende Klasse eine Verantwortung und das ist eine gewisse Funktionalität." Ein Objekt, das über seine Thread-Sicherheit weiß, ist dem Wissen darüber ähnlich, dass es in einer Linked-List oder in einem Web-Container ist. Es weiß, wo es sozusagen lebt, und für Objekte, das ist nicht gut. Ich werde es versuchen und sehen, wie es geht. –

Verwandte Themen