2017-10-17 14 views
0

가변적 인 시간이 걸리는 복잡한 계산이 있습니다. 일부 입력 값을 사용하면 1 초에 1000 단계를 수행 할 수 있습니다. 다른 입력 값을 사용하면 몇 초가 걸립니다.GUI가 계산 시간이 오래 걸리고 전체 프로세스가 느려짐

완전히 정확하므로 사용자에게 진행 상황을 알리고 싶습니다. 문제는 이전의 경우 GUI를 업데이트하는 것이 실제 계산보다 오래 걸린다는 것입니다. 따라서 완료된 후 대기열에 GUI 업데이트 이벤트가 약 10 초 (이 경우 전체 계산의 실행 시간을 3 배로 늘림) .

그게 일반적인 문제라고 생각, 그래서 다소 틀에 얽매이지 예에 고장 :

public class QueueTest { 

    static final int STEPS = 30; 

    public static void main(String[] args) { 
     final Gui gui = // ... 

     final Display display = Display.getDefault(); 
     final Thread thread = new Thread(() -> { 
      for (int i = 0; i < STEPS; i++) { 
       final int step = i; // calculate something etc. 
       gui.updateLater(display, step); 
      } 
      System.out.println("Finished calculation."); 
     }); 
     thread.start(); 

     while (true) { 
      if (!display.readAndDispatch()) { 
       display.sleep(); 
      } 
     } 
    } 

    interface Gui { 

     default void updateLater(Display display, int step) { 
      display.asyncExec(() -> update(step)); 
     } 

     default void update(int step) { 
      System.out.println("Update " + (step + 1) + "/" + STEPS); 
      if (step == STEPS - 1) { 
       System.out.println("Finished GUI."); 
      } 
     } 
    } 
} 

(추가 Thread은 단계를 "계산"과 진행 상황을 표시하는 GUI로 전송 .)

그럼 Gui의 일부 구현을 살펴 보자 :

static class NoGui implements Gui { 

    @Override 
    public void update(int step) { 
     if (step == STEPS - 1) { 
      System.out.println("Finished GUI."); 
     } 
    } 
} 

이 예제는 GUI가 완료 될 때만 인쇄합니다. 결과는 거의 동시에 인쇄되는 두 줄입니다.

Finished calculation. 
Finished GUI. 

매우 합리적입니다. GUI 이벤트는 빨리 완료됩니다. 이제 느린을 만들어 보자 :

static class SlowGui implements Gui { 

    @Override 
    public void update(int step) { 
     try { 
      Thread.sleep(100); 
      Gui.super.update(step); 
     } catch (final InterruptedException e) { 
      e.printStackTrace(); 
     } 
    } 
} 

이 계산의 마지막 3 초 떨어져 GUI로, 다음과 같은 것을 인쇄 : 나는 우리의 응용 프로그램에서보고 있어요 무엇

Finished calculation. 
Update 1/30 
Update 2/30 
Update 3/30 
... 
Update 30/30 
Finished GUI. 

. 계산이 완료되었지만 GUI가 너무 느리고 계산이 이미 완료된 후 이벤트 대기열을 실행해야합니다.

나는이 동작을 최적화하려는이 같은 내놓았다 :

Finished calculation. 
Update 1/30 
Update 30/30 
Finished GUI. 

이 구현은 단지 사이 등의 이벤트를 무시 :

static class IgnorantGui extends SlowGui { 

    private boolean inProgress; 
    private Integer nextStep; 

    @Override 
    public void updateLater(Display display, int step) { 
     if (this.inProgress) { 
      this.nextStep = Integer.valueOf(step); 
     } else { 
      this.inProgress = true; 
      super.updateLater(display, step); 
     } 
    } 

    @Override 
    public void update(int step) { 
     try { 
      Integer currentStep = Integer.valueOf(step); 
      do { 
       super.update(currentStep.intValue()); 
       currentStep = this.nextStep; 
       this.nextStep = null; 
      } while (currentStep != null); 
     } finally { 
      this.inProgress = false; 
     } 
    } 
} 

출력은 다음 네 줄입니다 훨씬 빠릅니다. 이것은 내 문제에 대한 유효한 해결책이다.

이 전체적인 사용 사례가 일반적 일 수 있다고 생각합니다. 아마도 더 좋은 해결책이 될 것입니다. 또는이를 처리 할 수있는 표준 Java API도 있습니다. (아마도 SWT/Eclipse Framework API 일 것입니다.)

그래서 ... 계산보다 업데이트하는 데 시간이 오래 걸리므로 응용 프로그램이 느려지는 GUI를 처리하는 방법은 무엇입니까?

+0

GUI 업데이트를 처리하기 위해'invokeLater()'를 사용한다고 가정합니다. – Kayaman

+0

@Kayaman'invokeLater()'가 스윙입니다, 그렇죠? 우리는 SWT를 사용하고 있습니다. –

+0

내 기억이 올바르게 작동한다면 스윙은 처리량을 향상시키기 위해 이벤트를 병합 할 수 있습니다. 나는 SWT가 비슷한 것을 가지고 있다고 생각한다. 귀하의 예는 너무 추상적입니다. 사용중인 실제 기술을 사용하여 [MCVE] (https://stackoverflow.com/help/mcve)를 만들고이를 해결해보십시오. – Kayaman

답변

2

내가 사용하는 한 가지 방법은 UI 스레드에서 타이머 실행 가능을 사용하여 백그라운드 스레드를 폴링하는 것입니다. 이를 위해 Display.timerExec을 사용

display.timerExec(100, new Runnable() { 
    @Override 
    public void run() { 

    // TODO update UI from background thread details 

    // Run again 
    display.timerExec(100, this); 
    } 
}); 

배경 스레드는 그냥 UI 스레드가 액세스 할 수있는 데이터를 유지하고있는 asyncExec 전화를하지 않습니다.

0

잘 모르겠 으면 계속 GUI를 업데이트하고있는 것으로 보입니다. gui를 업데이트해야하는시기를 결정하는 카운터와 같은 것을 추가하십시오.이 볼 필요 밤은 경우 또는 모든 단계는

static class SlowGui implements Gui { 

@Override 
public void update(int step) { 
    try { 
     if(step%5==0){ 
      Gui.super.update(step); 
     } 
    } catch (final InterruptedException e) { 
     e.printStackTrace(); 
    } 
} 

같은}

이 매 5 단계를 업데이트해야보십시오.

그리고 왜 업데이트 방법에 잠이 있습니까?

희망을 부탁드립니다.

+0

문제는 계산이 GUI보다 느리면 모든 단계에서 업데이트가 문제가되지 않는다는 것입니다. 우리는 어떤 경우 있는지 미리 알지 못합니다. ... 그리고 느린 GUI를 시뮬레이트하기위한 수면이 있습니다. –

+0

calc 메소드가 GUI에 대한 참조를 제공하도록하십시오. 그런 다음 계산 방법 내에서 GUI를 호출하거나 변경하십시오. 그런 다음 계산이 느리거나 빠르면 상관 없습니다. 다음 계산은 GUI가 준비된 후에 시작됩니다. 많은 시간이 걸리는 경우 X 시간마다 업데이트하십시오. – Cryptor

+0

아니요. GUI를 섞어 놓으면 계산이 중단됩니다. 그리고 계산이 훨씬 느려질 것입니다. GUI 사용을 위해 이벤트를 필터링하는 것이 좋습니다. –