2017-08-11 2 views
0

J'ai essayé de trouver la réponse à mon problème ici sur SO, mais en raison de leur abondance et de leur diversité, j'ai été quelque peu confus. Voici ma question: mon application compare deux fichiers et imprime le résultat dans un Swing.JTextPane. J'appelle le code qui traite les fichiers avec un bouton et pour éviter de suspendre l'IU je traite chaque paire de fichiers avec un SwingWorker. Voici le code:En attente de la fin d'un SwingWorker avant d'en exécuter un autre

class ProcessAndPrintTask extends SwingWorker<Void, Void> { 
     private Report report; 
     Integer reportResult; 
     ProcessAndPrintTask(Report report) { 
      this.report = report; 
      reportResult = null; 
     } 

     @Override 
     protected Void doInBackground() { 

      try { 
       reportResult = report.getComparator().compareTwoFiles(new FileInputStream(new File(pathToReportsA + report.getFilename())), 
         new FileInputStream(new File(pathToReportsB + report.getFilename()))); 
      } 

      catch (IOException ex) { 
       ex.printStackTrace(); 
      } 

      return null; 
     } 

     @Override 
     protected void done() { 

      String message = report.getFilename() + ": "; 
      if (reportResult != null) { 
       switch (reportResult) { 
        case 1: 
         StyleConstants.setBackground(style, Color.GREEN); 
         try { 
          doc.insertString(doc.getLength(), message + "MATCH\n", style); 
         } 
         catch (BadLocationException ex) {ex.printStackTrace();} 
         break; 
        case 0: 
         StyleConstants.setBackground(style, Color.RED); 
         try { 
          doc.insertString(doc.getLength(), message + "NO MATCH\n\n", style); 
          try { 
           for (String s : report.getComparator().getDifferences(
             new FileInputStream(new File(pathToReportsA + report.getFilename())), 
             new FileInputStream(new File(pathToReportsB + report.getFilename())))) { 
            doc.insertString(doc.getLength(), s + "\n", style); 
           } 
          } catch (Exception ex) { 
           ex.printStackTrace(); 
          } 
         } 
         catch (BadLocationException ex) {ex.printStackTrace();} 
         break; 
        case -1: 
         StyleConstants.setBackground(style, Color.CYAN); 
         try { 
          doc.insertString(doc.getLength(), message + "BOTH FILES EMPTY\n", style); 
         } 
         catch (BadLocationException ex) {ex.printStackTrace();} 
         break; 
        default: 
         StyleConstants.setBackground(style, Color.ORANGE); 
         try { 
          doc.insertString(doc.getLength(), message + "PROBLEM\n", style); 
         } 
         catch (BadLocationException ex) {ex.printStackTrace();} 

       } 
      } 
      else { 
       StyleConstants.setBackground(style, Color.ORANGE); 
       try { 
        doc.insertString(doc.getLength(), message + "FILE OR FILES NOT FOUND\n", style); 
       } 
       catch (BadLocationException ex) {ex.printStackTrace();} 
      } 

     } 
    } 

Le doInBackground()-ce que la comparaison, done() formate le message en fonction du résultat et des impressions de la comparaison il. Le problème est que le programme n'attend pas qu'une paire soit traitée ET imprimée pour que les résultats ne soient pas imprimés dans l'ordre dans lequel ils sont ouverts, ce qui peut être très déroutant pour l'utilisateur: la plupart des fichiers sont petits et passent vraiment Ainsi, la comparaison semble être terminée à un moment donné, mais des fichiers plus importants sont en cours de traitement.

Je lis au sujet de la possibilité d'utiliser un PropertyChangeListener mais je ne vois pas comment elle diffère selon la méthode done() ... J'ai essayé de faire à la fois la comparaison et l'impression en doInBackground() mais bousille la mise en forme (qui doit être attendu - avant que l'impression soit terminée, la couleur d'arrière-plan est modifiée). J'ai aussi essayé d'invoquer Thread.sleep() pour un montant arbitraire de temps dans la boucle qui appelle la SwingWorker qui ressemblait à ceci:

try (FileInputStream reportListExcelFile = new FileInputStream(new File(reportListPath))) { 
       Workbook workbook = new XSSFWorkbook(reportListExcelFile); 
       Sheet sheet = workbook.getSheetAt(0); 
       Iterator<Row> iter = sheet.iterator(); 

       // skip first row that contains columns names 
       iter.next(); 

       while (iter.hasNext()) { 
        try {Thread.sleep(1000);} catch (Exception ex) {ex.printStackTrace();} 
        Row r = iter.next(); 
        String name = r.getCell(0).getStringCellValue(); 
        String format = r.getCell(1).getStringCellValue(); 
        Report currentReport = new Report(name, format); 
        new ProcessAndPrintTask(currentReport).execute(); 
       } 
      } 

Non seulement il semble être une béquille laide mais aussi causé l'interface graphique pour accrocher jusqu'à ce que tout le fichier les paires ont été comparées.

Existe-t-il une solution?

+1

Il vous suffit de commencer votre deuxième 'SwingWorker' de la méthode' done' du premier Vous pouvez également utiliser un seul 'SwingWorker' et démarrer les deux tâches dans une seule méthode' doInBackground'. –

+0

Mais alors je devrais passer une collection de 'Report' à' SwingWorker', et faire l'itération à l'intérieur du travailleur, est-ce correct? – DCzo

+0

Essayé, a parfaitement fonctionné - merci! – DCzo

Répondre

1

Une fois que j'ai fait un OrderedResultsExecutors qui maintient l'ordre d'ajouter des tâches avec l'ordre de notifier les résultats. Tout ce que vous avez à faire est de mettre en œuvre la méthode de notification pour votre cas, par exemple. écrire quelques Listener ou quelque chose. Bien sûr, vous pouvez passer une collection de Report au SwingWorker et les traiter dans une boucle for, mais dans ce cas, vous perdrez le multithreading, et toutes les tâches pourraient prendre beaucoup plus de temps à s'exécuter d'une telle manière. Voilà pourquoi il pourrait être préférable d'avoir un rallye la version multithread de ce mécanisme, comme celui-ci:

Import java.util.concurrent.BlockingQueue; 
import java.util.concurrent.ConcurrentHashMap; 
import java.util.concurrent.ConcurrentLinkedDeque; 
import java.util.concurrent.ExecutionException; 
import java.util.concurrent.ExecutorService; 
import java.util.concurrent.Future; 
import java.util.concurrent.LinkedBlockingQueue; 
import java.util.concurrent.ThreadPoolExecutor; 
import java.util.concurrent.TimeUnit; 
import java.util.concurrent.atomic.AtomicLong; 

public class OrderedResultsExecutors extends ThreadPoolExecutor { 
    public OrderedResultsExecutors(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, 
      BlockingQueue<Runnable> workQueue) { 
     super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue); 
    } 

    private ConcurrentHashMap<Long, Runnable> startedTasks = new ConcurrentHashMap<>(); 
    private ConcurrentLinkedDeque<Runnable> finishedTasks = new ConcurrentLinkedDeque<>(); 
    private AtomicLong toNotify = new AtomicLong(0); 
    private AtomicLong submitedCount = new AtomicLong(0); 

    @Override 
    protected void beforeExecute(Thread t, Runnable r) { 
     super.beforeExecute(t, r); 
     startedTasks.put(submitedCount.getAndIncrement(), r); 
    } 

    @Override 
    protected void afterExecute(Runnable r, Throwable t) { 
     super.afterExecute(r, t); 
     finishedTasks.add(r); 
     finishedTask(); 
    } 

    private void finishedTask() { 
     Runnable orderedResult; 
     long current; 
     while ((orderedResult = startedTasks.get(current = toNotify.get())) != null 
       && finishedTasks.contains(orderedResult) && (orderedResult = startedTasks.remove(current)) != null) { 
      finishedTasks.remove(orderedResult); 
      notify(current, orderedResult); 
      toNotify.incrementAndGet(); 
     } 
    } 

    private void notify(long order, Runnable result) { 
     try { 
      System.out.println("order: " + order + " result: " + ((Future)result).get()); 
     } catch (InterruptedException | ExecutionException e) { 
      e.printStackTrace(); 
     } 
    } 

    public static ExecutorService newFixedThreadPool(int noOfThreads) { 
     int corePoolSize = noOfThreads; 
     int maximumPoolSize = noOfThreads; 
     return new OrderedResultsExecutors(corePoolSize, maximumPoolSize, 0L, TimeUnit.MILLISECONDS, 
       new LinkedBlockingQueue<Runnable>()); 
    } 

} 
+0

Merci, mais j'espérais une solution de plus haut niveau, et c'est ce que Serqiy Medvynskyy a suggéré dans son commentaire. – DCzo

+0

Si vous n'avez pas vraiment besoin de l'exécution parallèle de ces tâches (aka ils prennent tous très peu de temps) alors la soultion fournie par @SergiyMedvynskyy est vraiment bonne et simple. –

+0

C'est vrai - l'exécution parallèle n'était pas vraiment nécessaire. La partie importante pour moi était de garder l'interface sensible. Merci quand même :). – DCzo

0

La réponse (suggérée par Sergiy Medvynskyy) est de se débarrasser de la « infiniment beaucoup » SwingWorkers appelé dans une boucle, faites-en une qui utilise une liste d'éléments à traiter et imprimer et faire la boucle à l'intérieur du doInBackground().

Le code après refactoring ressemble à ceci:

class ProcessAndPrintTask extends SwingWorker<Void, Void> { 
     private List<Report> reports; 
     Integer reportResult; 

     ProcessAndPrintTask(List<Report> reports) { 
      this.reports = reports; 

     } 

     @Override 
     protected Void doInBackground() { 
      for (Report report : reports) { 
       try { 
        reportResult = report.getComparator().compareTwoFiles(new FileInputStream(new File(pathToReportsA + report.getFilename())), 
          new FileInputStream(new File(pathToReportsB + report.getFilename()))); 
       } catch (IOException ex) { 
        ex.printStackTrace(); 
       } 
       String message = report.getFilename() + ": "; 
       if (reportResult != null) { 
        switch (reportResult) { 
         case 1: 
          StyleConstants.setBackground(style, Color.GREEN); 
          try { 
           doc.insertString(doc.getLength(), message + "MATCH\n", style); 
          } catch (BadLocationException ex) { 
           ex.printStackTrace(); 
          } 
          break; 
         case 0: 
          StyleConstants.setBackground(style, Color.RED); 
          try { 
           doc.insertString(doc.getLength(), message + "NO MATCH\n\n", style); 
           try { 
            for (String s : report.getComparator().getDifferences(
              new FileInputStream(new File(pathToReportsA + report.getFilename())), 
              new FileInputStream(new File(pathToReportsB + report.getFilename())))) { 
             doc.insertString(doc.getLength(), s + "\n", style); 
            } 
           } catch (Exception ex) { 
            ex.printStackTrace(); 
           } 
          } catch (BadLocationException ex) { 
           ex.printStackTrace(); 
          } 
          break; 
         case -1: 
          StyleConstants.setBackground(style, Color.CYAN); 
          try { 
           doc.insertString(doc.getLength(), message + "BOTH FILES EMPTY\n", style); 
          } catch (BadLocationException ex) { 
           ex.printStackTrace(); 
          } 
          break; 
         default: 
          StyleConstants.setBackground(style, Color.ORANGE); 
          try { 
           doc.insertString(doc.getLength(), message + "PROBLEM\n", style); 
          } catch (BadLocationException ex) { 
           ex.printStackTrace(); 
          } 

        } 
       } 
       else { 
        StyleConstants.setBackground(style, Color.ORANGE); 
        try { 
         doc.insertString(doc.getLength(), message + "FILE OR FILES NOT FOUND\n", style); 
        } 
        catch (BadLocationException ex) { 
         ex.printStackTrace(); 
        } 
       } 
      } 
      return null; 
     } 
    } 

Et ici j'appelle le SwingWorker.execute():

try (FileInputStream reportListExcelFile = new FileInputStream(new File(reportListPath))) { 
       Workbook workbook = new XSSFWorkbook(reportListExcelFile); 
       Sheet sheet = workbook.getSheetAt(0); 
       Iterator<Row> iter = sheet.iterator(); 
       java.util.List<Report> reports = new ArrayList<>(); 
       // skip first row that contains columns names 
       iter.next(); 

       while (iter.hasNext()) { 
        Row r = iter.next(); 
        String name = r.getCell(0).getStringCellValue(); 
        String format = r.getCell(1).getStringCellValue(); 
        Report currentReport = new Report(name, format); 
        reports.add(currentReport); 
       } 
       new ProcessAndPrintTask(reports).execute(); 
      } 

Ce n'est pas très joli mais ça fonctionne :)