我正在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();
}
}
考慮提供一個[runnable示例](https://stackoverflow.com/help/mcve),它演示了您的問題。這不是代碼轉儲,而是您正在做的事情的一個例子,它突出了您遇到的問題。這會減少混淆和更好的反應 – MadProgrammer
如果您的設置正確,則不會發生這種情況。如果您使用運行「Task」的'javafx.concurrent.Service',則默認情況下會在後臺線程中創建,這將防止任何GUI滯後。你可以發佈你的'服務'代碼嗎? –
@James_D我加了代碼。我使用了一個我製作的測試CSV文件,大約800,000行,90 MB。 – w1res