2015-09-14 66 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