2010-12-11 19 views
2

Ich musste eine Implementierung einer benutzerdefinierten Barrier-Klasse mithilfe von Sperren als Teil meiner Kursarbeit durchführen. Um meine LockBarrier Klasse zu testen, habe ich einen folgenden Testcode erstellt. Es funktioniert richtig, aber ich bin besorgt, ob das der richtige Weg ist. Könnten Sie bitte Verbesserungen vorschlagen, die ich tun kann, insbesondere die Strukturierung der Klassen? Ich denke, meine Art zu schreiben ist nicht die richtige. Irgendwelche Vorschläge sind willkommen.Entwerfen einer Testklasse für eine benutzerdefinierte Barriere

public class TestDriver 
{ 
     private static LockBarrier barrier; 

     static class Runnable1 implements Runnable 
     { 
      public Runnable1() 
      { } 

      public void run() 
      { 
       try 
       { 
        System.out.println(Thread.currentThread().getId()+" lazy arrived at barrier"); 
        Thread.sleep(10000); 
        barrier.await(); 
        System.out.println(Thread.currentThread().getId()+" passed barrier");   

       } 
       catch (InterruptedException ie) 
       { 
        System.out.println(ie); 
       } 
      }  

     } 

     static class Runnable2 implements Runnable 
     {  

      public Runnable2() 
      { } 

      public void run() 
      { 
       try 
       { 
        System.out.println(Thread.currentThread().getId()+" quick arrived at barrier"); 

        //barrier.await(1,TimeUnit.SECONDS); 
        barrier.await(); 
        System.out.println(Thread.currentThread().getId()+" passed barrier"); 
       }    
       catch (InterruptedException ie) 
       { 
        System.out.println(ie); 
       } 
      } 
     } 

     static class Runnable3 implements Runnable 
     { 
      public Runnable3() 
      { } 

      public void run() 
      { 
       try 
       { 
        System.out.println(Thread.currentThread().getId()+" very lazy arrived at barrier"); 
        Thread.sleep(20000); 
        barrier.await(); 
        System.out.println(Thread.currentThread().getId()+" passed barrier"); 
       }    
       catch (InterruptedException ie) 
       { 
        System.out.println(ie); 
       } 
      } 
     } 


     public static void main(String[] args) throws InterruptedException 
     { 
      barrier = new LockBarrier(3);   
      Thread t1 = new Thread(new TestDriver.Runnable1()); 
      Thread t2 = new Thread(new TestDriver.Runnable2()); 
      Thread t3 = new Thread(new TestDriver.Runnable3());   
      t1.start(); 
      t2.start(); 
      t3.start(); 

      t1.join(); 
      t2.join(); 
      t3.join(); 
     } 
} 
+0

es ist ziemlich ausführlich, aber die einzige Sache, die ich erwarten würde zu sehen ist etwas, das die LockBarrier benachrichtigt, damit ein anderer Thread fortfahren kann. Wenn Sie etwas wie die eingebaute Condition-Klasse tun, könnten Sie erwarten, dass ein Signal() aufgerufen wird. –

+0

signalAll() wird innerhalb der awarn() - Implementierung der LockBarrier ausgeführt. Wenn alle Fäden die Barriere erreicht haben, signalisieren Sie uns alles. Was würden Sie vorschlagen, es weniger wortreich zu machen? –

+0

Wie war meine Antwort unten? War es nützlich? – Toby

Antwort

0

Der Code sieht gut aus. Vielleicht können Sie die LockBarrier an die Runnable übergeben, anstatt sie außerhalb zu deklarieren.

15

Trenn-Concurrency für Ihre Klasse

Testing Sachen gleichzeitig ist hart (tm)! GOOS empfiehlt unter anderem, den Nebenläufigkeitsteil von den Teilen zu trennen, die etwas Arbeit machen. Also zum Beispiel, wenn Sie eine Scheduler hatten, die eine Aufgabe auf einem oder mehreren Threads planen soll. Sie können den Teil, der für das Threading zuständig ist, an Ihren Scheduler übergeben und einfach testen, ob der Scheduler korrekt mit diesem Objekt zusammenarbeitet. Dies ist mehr in einem klassischen Unit-Test-Stil.

Ein Beispiel mit einem `Scheduler ist here, dies verwendet ein Mock Framework, um zu helfen. Wenn Sie mit diesen Ideen nicht vertraut sind, keine Sorge, sie sind wahrscheinlich nicht relevant für Ihren Test.

Nachdem dies gesagt wurde, möchten Sie möglicherweise Ihre Klasse 'im Kontext' in einer Multi-Threading-Weise ausführen. Dies scheint die Art von Test zu sein, den Sie oben schreiben. Der Trick dabei ist, den Test deterministisch zu halten. Nun, ich sage das, Theres ein paar Möglichkeiten.

deterministische

Wenn können Sie Setup Ihren Test in einer deterministischen Art und Weise, um die Fortschritte, für die Bedingungen an den wichtigen Punkten warten, bevor er nach vorn zu erfüllen, können Sie versuchen, eine bestimmte Bedingung zu simulieren zu testen. Das bedeutet, dass Sie genau verstehen, was Sie testen möchten (z. B. den Code in einen Deadlock zwingen) und deterministisch vorgehen (z. B. mit Abstraktionen wie CountdownLatches usw., um die beweglichen Teile zu "synchronisieren").

Wenn Sie versuchen, einen Multithread-Test zum Synchronisieren der sich bewegenden Teile durchzuführen, können Sie die Ihnen zur Verfügung stehende Concurrency-Abstraktion verwenden, aber es ist schwierig, weil es gleichzeitig ist; Dinge könnten in einer unerwarteten Reihenfolge passieren. Sie versuchen, dies in Ihrem Test mithilfe der sleep Anrufe mitegate. Wir schlafen normalerweise nicht gerne in einem Test, weil es den Test langsamer laufen lässt und wenn Tausende von Tests ausgeführt werden, zählt jede Minute. Wenn Sie den Schlafzeitraum zu sehr verringern, wird der Test nicht deterministisch und die Reihenfolge ist nicht garantiert.

Einige Beispiele hierfür sind

Sie haben eine der gotchas gesichtet, wo der Haupttest Thread vor den neu gelaicht Fäden im Test abschließen wird abgeschlossen (mit der join). Eine andere Möglichkeit besteht darin, auf eine Bedingung zu warten, z. B. mit WaitFor.

Soak/Load Testing

Eine andere Wahl Setup ist ein Test einrichten, ausführen und Spam-Klassen in einem Versuch, sie zu überlasten und sie zu zwingen, einige subtile Concurrency Problem zu verraten. Hier müssen Sie genau wie im anderen Stil eine bestimmte Assertion einrichten, damit Sie erkennen können, ob und wann die Klassen sich selbst verraten haben.

Für Ihren Test dann, würde ich vorschlagen, eine Behauptung zu kommen, so dass Sie positive und negative Läufe gegen Ihre Klasse sehen und die sleep (und system.out Anrufe ersetzen können. Wenn Sie können, führen Sie Ihren Test aus so etwas wie JUnit ist eigenwillig.

zum Beispiel kann ein Grund Test in der Art, die Sie begonnen haben, nach unten wie diese

public class TestDriver { 

    private static final CyclicBarrier barrier = new CyclicBarrier(3); 
    private static final AtomicInteger counter = new AtomicInteger(0); 

    static class Runnable1 implements Runnable { 
     public void run() { 
      try { 
       barrier.await(); 
       counter.getAndIncrement(); 
      } catch (Exception ie) { 
       throw new RuntimeException(); 
      } 
     } 

    } 

    @Test (timeout = 200) 
    public void shouldContinueAfterBarrier() throws InterruptedException { 
     Thread t1 = new Thread(new Runnable1()); 
     Thread t2 = new Thread(new Runnable1()); 
     Thread t3 = new Thread(new Runnable1()); 
     t1.start(); 
     t2.start(); 
     t3.start(); 
     t1.join(); 
     t2.join(); 
     t3.join(); 
     assertThat(counter.get(), is(3)); 
    } 
} 

Wenn möglich aussehen könnte, das Hinzufügen einer Zeitüberschreitung zu Ihrem Barrier ist eine gute Übung und würde helfen, einen negativen Test zu schreiben wie dieser

public class TestDriver { 

    private static final CyclicBarrier barrier = new CyclicBarrier(3); 
    private static final AtomicInteger counter = new AtomicInteger(0); 

    static class Runnable1 implements Runnable { 
     public void run() { 
      try { 
       barrier.await(10, MILLISECONDS); 
       counter.getAndIncrement(); 
      } catch (Exception ie) { 
       throw new RuntimeException(); 
      } 
     } 
    } 

    @Test (timeout = 200) 
    public void shouldTimeoutIfLastBarrierNotReached() throws InterruptedException { 
     Thread t1 = new Thread(new Runnable1()); 
     Thread t2 = new Thread(new Runnable1()); 
     t1.start(); 
     t2.start(); 
     t1.join(); 
     t2.join(); 
     assertThat(counter.get(), is(not((3)))); 
    } 

} 

Wenn Sie Ihre Implementierung veröffentlichen möchten, können wir möglicherweise mehr Alternativen vorschlagen. Ich hoffe, dass, obwohl Ihnen einige Ideen gibt ...

EDIT: Ein andere Wahl in dem Sperrobjekt für feinkörnige Behauptung zu erreichen, ist zum Beispiel

@Test (timeout = 200) 
public void shouldContinueAfterBarrier() throws InterruptedException, TimeoutException { 
    Thread t1 = new Thread(new BarrierThread(barrier)); 
    Thread t2 = new Thread(new BarrierThread(barrier)); 
    Thread t3 = new Thread(new BarrierThread(barrier)); 
    assertThat(barrier.getNumberWaiting(), is(0)); 
    t1.start(); 
    t2.start(); 
    waitForBarrier(2); 
    t3.start(); 
    waitForBarrier(0); 
} 

private static void waitForBarrier(final int barrierCount) throws InterruptedException, TimeoutException { 
    waitOrTimeout(new Condition() { 
     @Override 
     public boolean isSatisfied() { 
      return barrier.getNumberWaiting() == barrierCount; 
     } 
    }, timeout(millis(500))); 
} 

EDIT: ich http://tempusfugitlibrary.org/recipes/2012/05/20/testing-concurrent-code/ einige dieses oben geschrieben

+0

Alle Links zum Quellcode sind nicht mehr gültig; '( – Pod

+1

@Pod Ich habe sie aktualisiert, damit sie jetzt alle funktionieren sollten. Danke :) – Toby

Verwandte Themen