Reputation: 43
In a javafx application, I have a method retrain(String) which takes a long time on large input due to naive implementation (educational). I'm opening a dialog when it is loading and I'd like the user to be able to cancel/close out the dialog and the task will quit. Is there any way to do this without passing a reference to the task to retrain. Right now I think that when I cancel the thread is cancelled but retrain is still running in the background.
Any tips would be greatly appreciated!
@FXML
private void handleMarkovText() {
textgen.MarkovTextGenerator mtg = mainApp.getMTG();
Task<textgen.MarkovTextGenerator> task = new Task<textgen.MarkovTextGenerator>() {
@Override
public textgen.MarkovTextGenerator call() {
// process long-running computation, data retrieval, etc...
mtg.retrain(textBox.getText());
return mtg;
}
};
Dialog<Void> loadDialog = new Dialog<Void>();
loadDialog.setOnCloseRequest( e -> {
//if(task.isRunning()) {
task.cancel();
//}
});
loadDialog.setContentText("Training MTG...");
task.setOnRunning( e -> {
ButtonType cancelButtonType = new ButtonType("Cancel", ButtonData.CANCEL_CLOSE);
//Dialog<Void> loadDialog = new Dialog<Void>();
loadDialog.getDialogPane().getButtonTypes().add(cancelButtonType);
loadDialog.show();
if(task.isCancelled()) {
}
});
task.setOnSucceeded(e -> {
loadDialog.close();
textgen.MarkovTextGenerator result = task.getValue();
mainApp.showMarkovDialog(result);
// update UI with result
});
task.setOnCancelled(e -> {
System.out.println("Cancelled");
});
task.setOnFailed(e -> {
//System.out.println("failed");
});
//ProgressBar bar = new ProgressBar();
//bar.progressProperty().bind(task.progressProperty());
Thread thread = new Thread(task);
thread.start();
// train/retrain markov
// show results
}
Upvotes: 2
Views: 3442
Reputation: 159486
Handling task cancellation in your retrain
method
Your retrain
method needs to be aware of the Task cancellation status. It can do this by polling task.isCancelled
at appropriate times. If it detects a cancellation, the rewrite method can clean up after itself (releasing any resources it is consuming and ensuring that cancelled state is semantically consistent for your application) and not doing any further processing (effectively allowing the thread performing the rewrite to exit).
Example code
Example code for a cancellation check is provided in the Task documentation titled: "A Simple Loop With Progress Notification And Blocking Calls".
Background information
Relevant information from the Task javadoc:
In Java there is no reliable way to "kill" a thread in process. However, when cancel is called on a Task, it is important that the Task stop processing. A "run-away" Task might continue processing and updating the message, text, and progress properties even after the Task has been cancelled! In Java, cancelling a Task is a cooperative endeavor. The user of the Task will request that it be cancelled, and the author of the Task must check whether is has been cancelled within the body of the call method. There are two ways this can be done. First, the Task author may check the isCancelled method, inherited from FutureTask, to see whether the Task has been cancelled. Second, if the Task implementation makes use of any blocking calls (such as NIO InterruptibleChannels or Thread.sleep) and the task is cancelled while in such a blocking call, an InterruptedException is thrown. Task implementations which have blocking calls should recognize that an interrupted thread may be the signal for a cancelled task and should double check the isCancelled method to ensure that the InterruptedException was thrown due to the cancellation of the Task.
Using a Thread's interrupt Status
If you don't want to have your retrain
method reference the task API (because you want to decouple it from the the JavaFX task API or for other reasons), you may be able to achieve similar behavior to the task.isCancelled
check by invoking Thread.currentThread().isInterrupted()
from appropriate places in your retrain
method.
Such an approach is exemplified by Listing 5: PrimeProducer in the Goetz article listed below.
JavaFX Tasks, implement futureTask, so you can invoke cancel(boolean mayInterruptIfRunning)
on them with mayInterruptIfRunning
set to true and that should set an interrupt status on the thread executing your task (in your case it will be executing the rewrite
method).
Background information on general procedures for handling cancellation of threaded work: Java theory and practice: Dealing with InterruptedException (an article by Brian Goetz.
Note: whether you use a task cancellation or a thread interrupted status check, in either case it your custom the rewrite
method which you need to modify to make it handle cancellation or interruption appropriately.
Upvotes: 2