2015-09-14 72 views
2

我正在JavaFX中编写一个应用程序,需要偶尔加载大约1,000,000行(或更多)的大型CSV文件。当后台服务执行密集任务时,如何防止GUI滞后?

当用户单击按钮开始加载文件时,将启动服务加载内容,同时显示进度/取消对话框。 Service中的call()方法基本上是一个while循环,每次迭代都会从CSV文件中加载另一行。

问题是,当我启动服务时,进度条(不确定样式)变得不稳定。拖动对话框也很麻烦并且很慢。

我没有在网络上寻找解决方案的好运气,但最近发现的是将Thread.sleep()放入循环中,让GC等其他东西有机会赶上。

该解决方案似乎减少/消除了口吃,但它会增加很多时间来加载数据。我也猜测,不同处理器之间的准确睡眠时间会有所不同。

有什么方法可以动态地找出多久/经常睡觉吗?或者调用一些方法阻塞足够长的时间以保持GUI的响应?

为我服务的代码:

public class CSVLoadingService extends Service<List<ObservableList<DoubleProperty>>> { 
private ObjectProperty<File> srcFile = new SimpleObjectProperty<>(); 
private IntegerProperty startIndex = new SimpleIntegerProperty(0); 
private ObjectProperty<Character> csvDelimeter = new SimpleObjectProperty(CSVParser.DEFAULT_SEPARATOR); 
private DoubleProperty invalidCSVReplacement = new SimpleDoubleProperty(0); 
private ObjectProperty<Dialog> dialog = new SimpleObjectProperty<>(null); 

@Override 
protected Task<List<ObservableList<DoubleProperty>>> createTask() { 
    return new Task<List<ObservableList<DoubleProperty>>>() { 
     final ObjectProperty<File> _srcFile = srcFile; 
     final IntegerProperty _startIndex = startIndex; 
     final ObjectProperty<Character> _csvDelimeter = csvDelimeter; 
     final DoubleProperty _invalidCSVReplacement = invalidCSVReplacement; 

     @Override 
     protected ObservableList<ObservableList<DoubleProperty>> call() throws Exception { 
      if (_startIndex.getValue() < 0) 
       throw new IllegalArgumentException("Start index can't be negative."); 
      if (_srcFile.getValue() == null) 
       throw new IllegalStateException("File can't be null."); 

      final ObservableList<ObservableList<DoubleProperty>> result = FXCollections.observableArrayList(); 

      // Read the data from the CSV file. 
      try (final CSVReader reader = new CSVReader(new BufferedReader(new FileReader(_srcFile.getValue())), 
        _csvDelimeter.getValue(), 
        CSVParser.DEFAULT_QUOTE_CHARACTER, 
        _startIndex.getValue())) 
      { 
       // Read first line. 
       String[] csvLine = reader.readNext(); 

       // If there is actually data, then read the rest of it. 
       if (csvLine == null || csvLine.length == 0) { 
        result.clear(); 
       } else { 
        // Create columns. 
        for (String value : csvLine) { 
         result.add(FXCollections.observableArrayList()); 
        } 

        // Parse the CSV reads and add them to the columns. 
        int iteration = 0; 
        do { 
         int i = 0; 
         for (String value : csvLine) { 
          // Convert the string to a number and add it to the column. 
          try { 
           result.get(i).add(new SimpleDoubleProperty(Double.parseDouble(value))); 
          } catch (NumberFormatException|NullPointerException e) { 
           result.get(i).add(_invalidCSVReplacement); 
          } 
         } 

         iteration++; 
        } while (!isCancelled() && null != (csvLine = reader.readNext())); 
       } 
      } 

      return result; 
     } 
    }; 
} 

@Override 
protected void succeeded() { 
    super.succeeded(); 

    if (dialog.getValue() != null) { 
     dialog.getValue().close(); 
    } 
} 

@Override 
protected void failed() { 
    super.failed(); 

    if (dialog.getValue() != null) { 
     dialog.getValue().close(); 
    } 
} 
+2

考虑提供一个[runnable示例](https://stackoverflow.com/help/mcve),它演示了您的问题。这不是代码转储,而是您正在做的事情的一个例子,它突出了您遇到的问题。这会减少混淆和更好的反应 – MadProgrammer

+2

如果您的设置正确,则不会发生这种情况。如果您使用运行“Task”的'javafx.concurrent.Service',则默认情况下会在后台线程中创建,这将防止任何GUI滞后。你可以发布你的'服务'代码吗? –

+0

@James_D我加了代码。我使用了一个我制作的测试CSV文件,大约800,000行,90 MB。 – w1res

回答

-1

这是使用线程优先级的典型场景。您希望您的GUI线程具有比后台线程更高的优先级。

+0

为何这个回答被拒绝? – mipa

+0

你有任何例子或任何地方指出我会如何实现这一目标? (顺便说一句,我不是一个投下来。) – w1res

+0

这是一个覆盖主题的旧线程。 http://stackoverflow.com/questions/1617963/setting-priority-to-javas-threads – mipa