5

SlaveThread オブジェクトのリストを保持する Master があるとします。タイム ステップごとに、Master が SlaveThreads を並行して実行するようにしたいのですが、タイム ステップの最後に、SlaveThreads が互いに現在のタイム ステップを完了するのを待ってから先に進むようにします。また、各時間ステップで SlaveThreads を再インスタンス化したくありません。考えられる解決策は 2 つありますが、どちらも機能させる方法がわかりません。

1) SlaveThread の run() メソッドが while(true) ループ内にある。SlaveThread で 1 つのループを実行した後、SlaveThread に Master に通知してもらい (どうすればよいかわかりません)、Master は次のようなことを行います。

try{
    for (int i = 0; i < numSlaveThreads; i++) {
        while (!slaveThreads[i].getCompletedThisIter()) {
        wait()
        }
      }
  System.out.println("Joined");

}

次の時間ステップに進む前に。どうすればいいですか?単一の SlaveThread でマスターだけに通知するにはどうすればよいですか?

2) Slave の run() は while(true) ループにないため、反復ごとに start() を呼び出す必要があります。ただし、この時点でのスレーブのスレッド状態は終了します。再インスタンス化せずに start() を再度呼び出すにはどうすればよいですか?

4

2 に答える 2

5

CyclicBarrierまたはCountDownLatchでこれを実現できます。これらは、スレッドが計算を完了した場合に、目的の状態に達するまでスレッドの進行を遅らせるために使用されるシンクロナイザーです。

ここでは、どのように実現したいかの詳細によって異なります。

ラッチはイベントを待機するためのものです。バリアは他のスレッドを待つためのものです。

それCyclicBarrierは次のように行われます:

// whereby count is the number of your slave threads
this.barrier = new CyclicBarrier(count); 

次に、Runnableスレーブの定義で、計算の最後に挿入します。barrier.await()

public class Slaves implements Runnable {

   // ...

   @Override
   public void run() {

      while(condition) {

         // computation
         // ...

         try {
            // do not proceed, until all [count] threads
            // have reached this position
            barrier.await();
         } catch (InterruptedException ex) {
            return;
         } catch (BrokenBarrierException ex) {
            return;
         }
      }
   }
}

すべてのスレッドが計算を完了するまで、スレーブ スレッドは処理を続行しません。この方法では、別のマスター スレッド間のシグナリングを実現する必要はありません。

ただし、すべてのスレッドがその位置に到達した後に実行したいコードがある場合 (マスター シグナル) RunnableCyclicBarrierコンストラクターに追加を渡すことができます。これは、すべてのスレッドがバリアに到達した後に実行されます。

this.barrier = new CyclicBarrier(count,
   new Runnable() {
      @Override
      public void run() {
         // signal your master thread, update values, etc.
      }
    }
 );
于 2012-07-22T17:58:52.617 に答える
3

スレッドを管理するExecutorService (つまり、サイクルごとに新しいスレッドを作成することなくスレッドをリサイクルする) と、すべてのスレーブを同期するCyclicBarrierの組み合わせを使用できます。

マスターがループでスレーブを起動し、再起動する前にすべてが完了していることを確認する簡単な例を以下に示します。スレーブは少し怠け者で、(実際にはランダムではない) 一定時間スリープするだけです。

public class Test {

    private static final ExecutorService executor = Executors.newFixedThreadPool(5);
    private static final CyclicBarrier barrier = new CyclicBarrier(5); //4 slaves + 1 master

    public static void main(String[] args) throws InterruptedException {
        Runnable master = new Runnable() {
            @Override
            public void run() {
                try {
                    while (true) {
                        System.out.println("Starting slaves");
                        for (int i = 100; i < 500; i += 100) {
                            executor.submit(getRunnable(i));
                        }
                        barrier.await();
                        System.out.println("All slaves done");
                    }
                } catch (InterruptedException | BrokenBarrierException ex) {
                    System.out.println("Bye Bye");
                }
            }
        };

        executor.submit(master);
        Thread.sleep(2000);
        executor.shutdownNow();

    }

    public static Runnable getRunnable(final int sleepTime) {
        return new Runnable() {
            @Override
            public void run() {
                try {
                    System.out.println("Entering thread " + Thread.currentThread() + " for " + sleepTime + " ms.");
                    Thread.sleep(sleepTime);
                    System.out.println("Exiting thread " + Thread.currentThread());
                    barrier.await();
                } catch (BrokenBarrierException | InterruptedException ex) {
                }
            }
        };

    }
}
于 2012-07-22T17:59:19.683 に答える