2010-04-15 2 views
0

저는 Java 및 멀티 스레드를 처음 사용합니다. 다음과 같은 문제가 있습니다 :재귀/멀티 스레딩 프로그램 문제

두 개의 다른 스레드에서 실행되는 Class AClass B이라는 두 클래스가 있습니다.

Class AonNewEvent()입니다.

일단이 메서드가 호출되면 Class B에 작업을 요청합니다. B 클래스가 작업을 마치는 즉시 메서드를 호출합니다 (Class A에도 정의 됨).

여기에 문제가 있습니다. 내가 원하는 것은 onJobDone() 메서드에서 새 작업을 만들고 Class B으로 다시 보내야합니다. 여기

내가 실행

A.onNewEvent(){ 
    //create job 
    //ask B to do it 
    B.do() 
} 

B.do{ 
    // Do some stuff 
    A.jobDone() 
} 

A.onJobDOne(){ 
    B.do() //doItAgain 

    // print message "Thank you for doing it" 
} 

문제는 메시지가 "그 일을 주셔서 감사합니다"이다의 순서로 (의사) 무엇을 인쇄 결코 극복이다. 실제로 onJobDone() 메서드가 호출 될 때 B.do()이 매우 빠르기 때문에 B.do()을 호출합니다. 바로 onJobDone()을 호출하므로 실행 흐름이 코드의 인쇄 메시지 부분에 나타나지 않습니다.

이것은 불쾌한 멀티 스레딩 문제 중 하나라고 생각합니다.

도움을 주시면 감사하겠습니다.

+9

이 문제는 멀티 스레딩과 관련이 없으며 방금 무한 재귀 호출을 수행했습니다. – kahoon

+0

onJobDone()에서 B.do() 전에 메시지를 인쇄하십시오. –

+1

B.do()가 암묵적으로 항상 B.Do()를 다시 호출하면 결코 종료되지 않습니다. 당신은 시간이 좀 stackoverflow 예외로 끝내야한다 –

답변

3

이것은 다중 스레드 문제가 아니며 방금 무한 루프를 만들었습니다. B.do는 A.onJobDone을 ​​호출하여 A.doJobDone 등을 호출하는 B.do를 호출합니다. 따라서 실행은 인쇄 메시지 행에 도달하지 않습니다. onJobDone 안에서 'doItAgain'을 원할지 여부를 결정할 수 있도록 브레이크 아웃 조건이 필요합니다. 어느 시점에서 다시하지 않기로 결정하면 그 시점에서 코드가 인쇄 메시지 라인에 도달하게됩니다.

달성하려는 목표를 설명하고 도움을주는 가장 좋은 방법에 대해 알려줄 수 있습니다. 나는 당신이 당신의 문제를 해결하려고 노력하는 방법이 정말로 최선의 방법인지 확실하지 않습니다.

+0

그가 멀티 스레딩 경로를 선택한 이유는 일단 클래스 B로 작업을 보내면 ... 클래스 A에서 정상적인 실행 흐름을 계속할 수 있습니까? 메서드 내 마지막 명령문이 실행되기 전에 thread B가 onJobDone()을 호출하지 않도록 지정할 수 있습니까? – Chronos

+0

실제 코드를 게시 할 수 있습니까?이 클래스의 다중 스레드 기능을 구현하는 방법과 클래스 A의 정상적인 실행 흐름을 계속 수행하는 방법에 대해 알고 싶습니다. (의사 코드는 다른 실행을 나타내지 않습니다. 직업 완료 이벤트를 청취하는 것 이외의 클래스 A에서). – DaveJohnston

0

다음은 본질적으로 스레드에 연결되지 않은 클래스 메소드를 호출 제안

A.onNewEvent(){ 
    //create job 
    //ask B to do it 
    B.do() 

} 

B.do{ 
    // Do some stuff 
    A.onJobDone() 
} 

A.onJobDone(){ 
    // print message "Thank you for doing it" (B.do() has just completed, so it 
    //will print for every time you call B.do() 
    if (shouldDoJobAgain()) //it should stop some time, right? 
     B.do() 

}

+0

그는 멀티 스레딩 경로를 선택한 이유는 일단 클래스 B로 작업을 보내면 ... 클래스 A에서 정상적인 실행 흐름을 계속할 수 있습니까? 메서드 내 마지막 명령문이 실행되기 전에 thread B가 onJobDone()을 호출하지 않도록 지정할 수 있습니까? – Chronos

+0

onJobDone()에서 onNewEvent() ...가 아니라 실행을 원하는 횟수를 결정하는 논리가 정의됩니다. 가끔은 작업을 실행하기 위해 보낼 수는 있지만 수행 할 수 없기 때문입니다. – Chronos

+0

@ 크로노스가 더 좋아 보이십니까? –

0

입니다. 즉, 스레드 1에서 B.do()A.onNewEvent()으로 호출하면 여전히 스레드 1에서 실행됩니다. 다른 말로하면 객체의 메서드는 모든 스레드에서 호출 할 수 있습니다.

주어진 스레드 내에서 메소드를 실행하려면 스레드의 run 메소드 내에서 해당 메소드를 호출해야합니다. 이를 지원하기 위해서는 새로운 작업이있을 때 신호를 보내는 B의 메소드, 작업 정보를 보유 할 B의 필드 (일반적으로 대기열을 사용하지만 간단한 정수는 여기에서 작동 할 수 있음)를 정의해야합니다. 제대로 B의 필드에 대한 액세스를 동기화합니다. Java (경쟁 조건이나 어딘가 교착 상태가있을 수 있음)에서이 작업을 수행 한 이래 조금 있었지만 이어야합니다.

class A { 
    ... 
    public void onEvent() { 
     b.addJob(); 
    } 
    // careful about making onJobDone synchronized 
    public void onJobDone() { 
     // do it again 
     b.addJob(); 
     ... 
    } 
} 

class B extends Thread { 
    A a; 
    int jobCount; 
    boolean work; 

    ... 
    public void run() { 
     shouldWork(true); 
     while (shouldWork()) { 
      while (haveJobs()) { 
       doingJob(); 
       ... 
       didJob(); 
      } 
      waitForWork(); 
     } 
    } 
    public synchronized void addJob() { 
     ++jobCount; 
     notify(); 
    } 
    protected synchronized boolean haveJobs() { 
     return jobCount > 0; 
    } 
    protected synchronized void doingJob() { 
     /* could also decrement 'jobCount' in didWork(), in which case 
      it will need to be made synchronized 
     */ 
     --jobCount; 
    } 
    protected void didJob() { 
     a.onJobDone(); 
    } 
    protected synchronized void waitForWork() { 
     while (! haveJobs()) { 
      try { 
       wait(); 
      } catch (Exception e) { 
     } 
    } 
    public synchronized void shouldWork(boolean w) { 
     work = w; 
    } 
    protected synchronized boolean shouldWork() { 
     return work; 
    } 
} 

주요 문제는 교착 상태입니다. 대부분의 동기화 된 메소드는 교착 상태를 발생시키지 않습니다.waitForWorkwait을 호출하여 모니터를 해제하므로 다른 스레드가 addJob을 성공적으로 호출 할 수 있습니다.

작업 종료로 인해 새 작업이 예약되므로 위의 사항은 계속 실행됩니다. 코딩 방법에 대한 이점은 A.onJobDone이 완료되고 스택 오버플로가 발생하지 않는다는 것입니다. 각 A.onEvent가, B는 n 개의 작업을 처리하는 원인으로 A을 정의하려면 :

class A { 
    int jobsPerEvent; 
    int remainingJobs; 
    ... 
    public void onEvent() { 
     synchronized (this) { 
      remainingJobs += jobsPerEvent; 
     } 
     b.addJob(); 
    } 
    public synchronized void jobsRemain() { 
     return remainingJobs > 0; 
    } 
    public void onJobDone() { 
     synchronized (this) { 
      --remainingJobs; 
     } 
     // do it again 
     if (jobsRemain()) { 
      b.addJob(); 
     } 
     ... 
    } 
} 
0

또 다른 방법을 별도의 클래스로 비동기 방식 (B.do)를 리팩토링, asynchronous method 호출을 구현하는 것입니다.

class B { 
    A a; 
    Set jobs; 
    class BAlgo extends Thread { 
     public void run() { 
      // stuff originally in B.do 
      ... 
      done(); 
      jobs.remove(this); 
     } 
    } 

    public void do() { 
     BAlgo algo = this.new BAlgo(); 
     algo.start(); 
     jobs.add(algo); 
    } 
    protected void done() { 
     a.onJobDone(); 
    } 
    ... 
} 

나는 알고리즘이 다른 패턴 구현하는 클래스에 do 방법을 리팩토링 확신 해요,하지만 난 그 이름의 확실하지 않다. bridge pattern이 아니며 template method pattern도 아닙니다. 거의 proxy 패턴을 따르지만 꽤 아닙니다.