2012-07-22 3 views
5

SlaveThread 개체 목록을 유지하는 마스터가 있다고 가정합니다. 매 시간 단계마다 마스터가 슬레이브 스레드를 병렬로 실행하기를 원하지만, 타임 스텝이 끝날 때 슬레이브 스레드가 앞으로 나아갈 때까지 현재 시간 간격을 완료하기를 기다리 길 원합니다. 또한 각 시간 단계마다 SlaveThreads를 다시 설치하기를 원하지 않습니다. 두 가지 가능한 솔루션을 가지고 있으며 둘 중 하나를 작동시키는 방법을 모르겠습니다.Java - 시간 단계별 다중 스레드 동기화

1) SlaveThread의 run() 메서드는 while (true) 루프에 있습니다. SlaveThread의 단일 루프의 실행 후, 내가해야 SlaveThread은 (내가하는 방법을 모른다) 마스터에게 통지하고, 주인은 전에

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()를 호출 할 수 있습니까?

+0

환상적인 장벽을 원하는 것처럼 들리겠습니다. http://docs.oracle.com/javase/7/docs/api /java/util/concurrent/CyclicBarrier.html (변형 1 사용) – millimoose

답변

5

이것은 장벽이 무엇인지 정확히 나타내므로 CyclicBarrier 또는 CountDownLatch으로 알 수 있습니다. 이들은 동기화에 사용되는 원하는 상태에 도달 할 때까지 스레드의 진행을 지연, 귀하의 경우 스레드가 계산을 완료했습니다. 여기

당신이 실현하는 방법 세부 사항에 따라 달라집니다

래치 이벤트를 기다리는위한 것입니다; 장벽은 다른 스레드를 기다리는 것입니다. 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; 
     } 
     } 
    } 
} 

:

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

이 그런 다음 노예의 Runnable 정의에 당신이 계산의 마지막에 삽입합니다 : 다음과 같은 방식으로 할 것 CyclicBarrier를 들어

모든 스레드가 계산을 마칠 때까지는 슬레이브 스레드가 진행되지 않습니다. 이렇게하면 다른 마스터 스레드간에 신호를 인식 할 필요가 없습니다.

그러나 모든 스레드가 해당 위치 (마스터 시그널링)에 도달 한 후에 실행할 코드가있는 경우 RunnableCyclicBarrier 생성자에 전달할 수 있습니다.이 코드는 모든 스레드가 장벽에 도착한 후에 실행됩니다 .

this.barrier = new CyclicBarrier(count, 
    new Runnable() { 
     @Override 
     public void run() { 
     // signal your master thread, update values, etc. 
     } 
    } 
); 
+0

다른 스레드가 현재 반복으로 완료되었다는 것을 다른 스레드가 마스터에 알리기를 기다리고 싶습니다 (모든 스레드가 동시에 다음 스레드로 진행할 수 있도록합니다). 하나) – Trup

+0

주기적인 장벽을 반복 실행의 끝에서 카운트를 다시 시작하려면 어떻게합니까? 당신은 각 스레드가 await()를 호출하여 카운터가 1이 될 때까지 1 씩 감소한다고 말했지만 그 시점에서 numThreads로 카운터를 다시 시작하여 다음주기가 실행되도록합니다. – Trup

+0

@Trup 당신은 걱정할 필요가 없습니다. 자동적으로 이런 일이 생기므로,주기적인 장벽이라고 불리는 이유 때문에 원하는만큼 자주 재사용 할 수 있습니다. –

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

    } 
} 
+0

노예를위한 코드는 어느 것입니까? 또한 주기적 장벽과 실행 서비스가 없어도됩니까? 어떻게 보이는지 보여 주실 수 있습니까? 기본적으로 노예가 순환 장벽에 대해 어떻게 말하고 있는지, 그리고 마스터가 주기적 장벽을 사용하여 모든 노예가 끝날 때까지 기다리는 방법을 분명히보고 싶습니다. 감사! – Trup

+0

슬레이브의 실행 파일은'getRunnable (i)'에 의해 생성됩니다. 당신은 executorervice없이 cyclicbarrier를 사용할 수 있습니다. 올바른 번호로 장벽을 설정하기 만하면됩니다. 'barrier.await()'가 호출 될 때마다 호출 스레드가 대기하고 그 번호가 감소합니다. 숫자가 0이되면 대기중인 모든 스레드가 다시 작동하기 시작합니다. – assylias

+0

게시 된 프로그램을 실행하여 작동 방식을 확인하고 필요에 맞게 수정하는 것이 좋습니다. – assylias