2010-12-11 6 views
2

コースワークの一部としてロックを使用してカスタムバリアクラスの実装を行う必要がありました。 LockBarrierクラスをテストするには、次のテストコードを用意しました。それは正しく働いていますが、それが適切なやり方であるかどうかは心配です。私ができる改善、特にクラスの構造化をお勧めしますか?私は自分のコーディング方法が正しいとは思わない。どんな提案も大歓迎です。カスタムバリアのテストクラスの設計

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

しかし、私が見ると予想されるのは、LockBarrierに別のスレッドの処理を許可するよう通知するものです。組み込みのConditionクラスのようなことをしているのであれば、signal()が呼び出されることが期待されるでしょう。 –

+0

signalAll()は、LockBarrierのawait()実装内で実行されます。すべてのスレッドがバリアに到達したら、signalAllが完了しました。それほど冗長ではないとあなたは何を提案しますか? –

+0

私の答えはどうでしたか?それは役に立ちましたか? – Toby

答えて

0

コードは私によく見えます。 LockBarrierを外部に宣言するのではなく、Runnableに渡すことができます。あなたのクラスのための

15

Seperating同時実行を

テストのものは、同時にハード(tm)のです! GOOSは、並行処理部分をいくつかの作業を行っている部分から分離することを推奨しています。したがって、たとえば、1つまたは複数のスレッドでいくつかのタスクをスケジュールすることになっているSchedulerがあるとします。あなたのスケジューラーにスレッドを担当する部分を渡し、スケジューラーがこのオブジェクトと正しくコラボレートすることをテストするだけです。これは古典的な単体テストスタイルの方が多いです。

スケジューラを使用した例はhereです。これはモックフレームワークを利用しています。あなたがそれらのアイデアに慣れていない場合は、おそらくあなたのテストには関係ないと心配しないでください。

実際には、マルチスレッドの方法で 'コンテキスト内で'クラスを実行したいと思うかもしれません。これはあなたが上で書いているようなテストのようです。ここでのトリックは、テストを決定的に保つことです。まあ、私はそれが2つの選択肢をtheresと言う。

決定的

あなたはセットアップ決定論的な方法で進行するテストを、前進する前に満たすべき条件のためのキーポイントで待っている、あなたはテストに特定の条件をシミュレートしようとすることができます。これは、テストするものを正確に理解すること(例えば、コードをデッドロックにするなど)と確定的なステップ(例えば、CountdownLatchesなどの抽象化を使用して動く部分を同期させる)を意味します。

マルチスレッドテストで動く部分を同期化しようとすると、実行可能な並行性抽象化を使用することはできますが、その並行処理は困難です。予期しない順序で事態が起こる可能性があります。 sleepコールを使用してテストでこの問題を解決しようとしています。私たちは一般的にテストで寝るのが好きではありません。テストが遅くなり、何千ものテストを実行すると、すべてのmsがカウントされるからです。スリープ時間をあまり長くしすぎると、テストが非決定的になり、順序が保証されません。

いくつかの例としては、あなたがメインのテストスレッドは、完全なテストの下で、新たに生み出されたスレッドの前に終了します落とし穴の1(発見しましたCountdownLatch

  • Setting up a thread to be interuptted
  • 使用

    • Forcing a deadlockが含まれますjoinを使用)。別の方法は、たとえばWaitForを使用して条件を待つことです。

      は別の選択は、セットアップにセットアップするテストである

      /負荷テストを浸し、それらをオーバーロードし、いくつかの微妙な同時実行の問題を裏切るためにそれらを強制しようとする試みで、あなたのクラスを実行し、スパム。ここでは、他のスタイルと同様に、クラスが自分自身を裏切ったかどうか、いつクラスを裏切ったかを知ることができるように、特定のアサーションをセットアップする必要があります。

      あなたがテストしているので、私はあなたのクラスに対してプラスとマイナスの両方を見ることができ、sleep(とsystem.outの呼び出しを置き換えることができるようにアサーションを提示することをお勧めします。 JUnitのようなものは、より特有である。例えば

      、あなたがこの

      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)); 
          } 
      } 
      

      可能な場合は次のようになりますダウンし始めましたスタイルで基本テスト、あなたの障壁にタイムアウトを追加することをお勧めし否定的なテストを書くのに役立ちますこのように

      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)))); 
          } 
      
      } 
      

      あなたが実装を投稿したい場合は、より多くの選択肢を提案できます。 EDIT

      @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それはあなたにいくつかのアイデアを与える願って、私はhttp://tempusfugitlibrary.org/recipes/2012/05/20/testing-concurrent-code/でこの一部を書きました

    +0

    ソースコードへのリンクはすべて有効ではなくなりました '( – Pod

    +1

    @Pod私はそれらを更新しました。 – Toby