2017-10-17 4 views
0

Nous avons un calcul complexe qui prend un temps variable. Avec quelques valeurs d'entrée, on peut faire mille étapes en une seconde - avec d'autres valeurs d'entrée, une étape prend plusieurs secondes.L'interface graphique prend plus de temps que le calcul et ralentit le processus entier

C'est tout à fait correct, nous voulons juste informer l'utilisateur de la progression. Le problème est que dans le premier cas, la mise à jour de l'interface graphique prend plus de temps que le calcul réel. Après cela, il reste environ 10 secondes d'événements de mise à jour dans la file d'attente (ce qui triple le temps d'exécution) .

Je pense que ce problème général, donc je l'ai cassé vers le bas un exemple agnostique un peu de cadre:

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

(Le Thread supplémentaire ne « calcule » pas et l'envoie à l'interface graphique pour montrer les progrès .)

Alors considérons certaines implémentations de Gui:

static class NoGui implements Gui { 

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

Cet exemple s'imprime uniquement lorsque l'interface graphique est terminée. Le résultat est ces deux lignes, imprimées presque au même moment:

C'est parfaitement raisonnable. Les événements GUI sont rapides à terminer. Maintenant, nous allons les faire lentement:

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

Cette imprime quelque chose comme ce qui suit, avec la finition de calcul et l'interface graphique trois secondes d'intervalle:

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

C'est ce que je vois dans notre application . Le calcul se termine, mais l'interface graphique est trop lente et doit exécuter sa file d'attente d'événements une fois le calcul effectué.

Je veux optimiser ce comportement et est venu avec quelque chose comme ceci:

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

La sortie est les quatre lignes suivantes:

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

Cette mise en œuvre ne tient pas compte que les événements entre les deux et ainsi est beaucoup plus rapide. C'est une solution valide à mon problème.

Je pense que tout ce cas d'utilisation peut être commun, et peut-être qu'il existe une solution plus élégante. Ou même une API Java standard pour le gérer. (Peut-être une API SWT/Eclipse Framework, puisque c'est ce que nous utilisons.)

Alors ... comment gérer une interface graphique qui met plus de temps à mettre à jour que le calcul et donc ralentit l'application?

+0

Je suppose que vous utilisez 'invokeLater()' pour gérer les mises à jour de l'interface graphique? – Kayaman

+0

@Kayaman 'invokeLater()' est Swing, n'est ce pas? Nous utilisons SWT. –

+0

Si ma mémoire est bonne, Swing a la capacité de fusionner des événements pour améliorer le débit. J'imagine que SWT a quelque chose de similaire. Votre exemple est trop abstrait. Créez un [MCVE] (https://stackoverflow.com/help/mcve) en utilisant la technologie que vous utilisez, et essayez de résoudre ce problème. – Kayaman

Répondre

2

Une façon que j'utilise est d'interroger le thread d'arrière-plan en utilisant une minuterie exécutable dans le thread UI. Utilisez Display.timerExec pour cela:

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

    // TODO update UI from background thread details 

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

Le fil d'arrière-plan ne fait pas de asyncExec appelle simplement les données que maintient le thread d'interface utilisateur peut accéder.

0

Je ne sais pas si je comprends bien, mais il semble que vous mettiez continuellement à jour l'interface graphique. Essayez d'ajouter quelque chose comme un compteur qui détermine quand le GUI doit être mis à jour.Ou si elle isnt avait besoin de voir toutes les étapes essayer quelque chose comme

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

}

cela devrait mettre à jour toutes les 5 étapes.

Et pourquoi y a-t-il une veille dans la méthode de mise à jour?

J'espère que je pourrais vous aider.

+0

Le problème est que si le calcul est plus lent que l'interface graphique, il n'y a aucun problème pour le mettre à jour à chaque étape. Nous ne savons pas à l'avance quel cas nous avons. ... et le sommeil est là pour simuler une interface graphique lente. –

+0

Essayez de donner à votre méthode de calcul une référence à votre interface graphique. Appelez ou modifiez votre interface graphique depuis votre méthode de calcul. Ensuite, peu importe si le calcul est lent ou rapide, le prochain calcul commencera après que le gui soit prêt. Si cela prend beaucoup de temps, laissez-le mettre à jour toutes les X fois. – Cryptor

+0

No. GUI de mélange et le calcul est un no-go. Et cela rendrait le calcul beaucoup plus lent. Je préfère filtrer les événements pour la consommation de l'interface graphique. –