タスクの新しいインスタンスが起動された場合にどのようにタスクをキャンセルするには?

ゼファー:

私のアプリケーションは含まListViewバックグラウンドタスクオフ項目が選択されるたびにそのキックを。それが正常に完了したときにバックグラウンドタスクは、UIに関する情報を更新します。

しかし、ユーザーはすぐに別の後に一つの項目をクリックすると、すべてのこれらのタスクを継続し、最後のタスク「勝利」を完了することと関係なく、最後に選択された項目の、UIを更新します。

私に必要なのはどういうわけか、このタスクのみを任意の時点で実行されていることの1つのインスタンスがあることを確認ので、新しいものを開始する前に、以前のすべてのタスクをキャンセルすることです。

ここでは、問題を示していMCVEは、次のとおりです。

import javafx.application.Application;
import javafx.concurrent.Task;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.ListView;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class taskRace  extends Application {

    private final ListView<String> listView = new ListView<>();
    private final Label label = new Label("Nothing selected");
    private String labelValue;

    public static void main(String[] args) {

        launch(args);
    }

    @Override
    public void start(Stage stage) throws Exception {

        // Simple UI
        VBox root = new VBox(5);
        root.setAlignment(Pos.CENTER);
        root.setPadding(new Insets(10));
        root.getChildren().addAll(listView, label);

        // Populate the ListView
        listView.getItems().addAll(
                "One", "Two", "Three", "Four", "Five"
        );

        // Add listener to the ListView to start the task whenever an item is selected
        listView.getSelectionModel().selectedItemProperty().addListener((observableValue, oldValue, newValue) -> {

            if (newValue != null) {

                // Create the background task
                Task task = new Task() {
                    @Override
                    protected Object call() throws Exception {

                        String selectedItem = listView.getSelectionModel().getSelectedItem();

                        // Do long-running task (takes random time)
                        long waitTime = (long)(Math.random() * 15000);
                        System.out.println("Waiting " + waitTime);
                        Thread.sleep(waitTime);
                        labelValue = "You have selected item: " + selectedItem ;
                        return null;
                    }
                };

                // Update the label when the task is completed
                task.setOnSucceeded(event ->{
                    label.setText(labelValue);
                });

                new Thread(task).start();
            }

        });

        stage.setScene(new Scene(root));
        stage.show();

    }
}

ランダムな順序で複数の項目をクリックすると、結果は予測不可能です。ラベルは、最後の結果を示すために更新するために何が必要なのであるTask実行されたことを。

私は何とかタスクをスケジュールするか、以前のすべてのタスクをキャンセルするためにサービスにそれらを追加する必要がありますか?

EDIT:

私の実際のアプリケーションでは、ユーザーから項目を選択ListViewし、バックグラウンドタスクは、データベース(複雑な読み込みSELECT、その項目に関連するすべての情報を取得するために文)。これらの詳細は、アプリケーションに表示されます。

起こっている問題は、ユーザが項目を選択するが、それらの選択を変更したときに、データが完全に異なるアイテムが現在選択されていても、選択された最初のアイテムのための可能なアプリケーションで表示戻されます。

最初から返されたデータ(すなわち:不要)選択は完全に破棄することができます。

なスロー:

あなたの要件は、この答えは言及し、使用のための完璧な理由のように見えますServiceAは、Serviceあなたが実行することを可能にするものを Task再利用可能で、任意の時点で1つの方法。あなたがキャンセルした場合Service、経由してService.cancel()、それは根本的なキャンセルTaskServiceまた、自身を追跡しTask 、あなたのためにあなたがリストのどこかでそれらを維持する必要はありません。

あなたがしたいと思いますどのようなあなたのMVCEを使用すると、作成されService、あなたを包み込みいますTaskたびにユーザーがで新しい項目を選択しListView、あなたがキャンセルしたいService、必要な状態を更新し、その後、再起動をService次に、使用したいService.setOnSucceededの結果を設定するコールバックをLabel最後の正常な実行があなたに返却されます。この保証。以前にキャンセルされた場合でもTask、S、まだ結果を返すServiceそれらを無視します。

また、(少なくとも、あなたのMVCEで)外部同期を心配する必要はありません。すべての操作は、起動キャンセル、および観察との契約というServiceFXのスレッドで起こります。(下の特色)コードのビットだけではない FXのスレッドで実行は内部になりますTask.call()(クラスがインスタンス化されますときに私はJavaFXの-ランチャースレッドで起こると信じているだけでなく、すぐに割り当てられたフィールド)。

ここで使用してMVCEの修正版ですService

import javafx.application.Application;
import javafx.concurrent.Service;
import javafx.concurrent.Task;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.ListView;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class Main extends Application {

    private final ListView<String> listView = new ListView<>();
    private final Label label = new Label("Nothing selected");

    private final QueryService service = new QueryService();

    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage stage) throws Exception {
        service.setOnSucceeded(wse -> {
            label.setText(service.getValue());
            service.reset();
        });
        service.setOnFailed(wse -> {
            // you could also show an Alert to the user here
            service.getException().printStackTrace();
            service.reset();
        });

        // Simple UI
        VBox root = new VBox(5);
        root.setAlignment(Pos.CENTER);
        root.setPadding(new Insets(10));
        root.getChildren().addAll(listView, label);

        // Populate the ListView
        listView.getItems().addAll(
                "One", "Two", "Three", "Four", "Five"
        );

        listView.getSelectionModel().selectedItemProperty().addListener((observableValue, oldValue, newValue) -> {
            if (service.isRunning()) {
                service.cancel();
                service.reset();
            }
            service.setSelected(newValue);
            service.start();
        });

        stage.setScene(new Scene(root));
        stage.show();

    }

    private static class QueryService extends Service<String> {

        // Field representing a JavaFX property
        private String selected;

        private void setSelected(String selected) {
            this.selected = selected;
        }

        @Override
        protected Task<String> createTask() {
            return new Task<>() {

                // Task state should be immutable/encapsulated
                private final String selectedCopy = selected;

                @Override
                protected String call() throws Exception {
                    try {
                        long waitTime = (long) (Math.random() * 15_000);
                        System.out.println("Waiting " + waitTime);
                        Thread.sleep(waitTime);
                        return "You have selected item: " + selectedCopy;
                    } catch (InterruptedException ex) {
                        System.out.println("Task interrupted!");
                        throw ex;
                    }
                }

            };
        }

        @Override
        protected void succeeded() {
            System.out.println("Service succeeded.");
        }

        @Override
        protected void cancelled() {
            System.out.println("Service cancelled.");
        }

    }
}

お問い合わせの際Service.start()は、作成Taskおよび実行それが現在使用してExecutorその中に含まれるエグゼキュータプロパティをプロパティが含まれている場合null、それはいくつかの未指定、デフォルトを使用しますExecutor(デーモンスレッドを使用します)。

上記は、あなたは私が呼んで見reset()キャンセルした後とではonSucceededonFailedコールバック。これは、あるServiceときにのみに開始することができるREADY 状態あなたは使用することができるrestart()のではなく、start()必要に応じて。それは基本的に呼び出すのと同じですcancel()> - reset()> - start()

1 resuableなりません。むしろ、新しい作成し、それを起動たびに。TaskServiceTask


あなたがキャンセルした場合Serviceには、現在実行中のキャンセルTaskがある場合、。にもかかわらずService、そのためTask、キャンセルされている実行が実際に停止したという意味ではありませんJavaでは、バックグラウンドタスクをキャンセルすると、タスクの開発者との協力が必要です。

この協力は、実行が停止した場合、定期的にチェックするという形をとります。ノーマルを使用している場合Runnableか、Callableこれが現在の割り込みステータスを確認する必要になるThreadか、いくつかの使用booleanフラグ2を以来Task拡張FutureTaskを使うこともできますisCancelled()から継承されたメソッドFutureのインターフェースを。あなたが使用できない場合はisCancelled()、何らかの理由で(外コードと呼ばれる、使用していないTask、あなたが使用してスレッドの中断を確認する...など、):

  • Thread.interrupted()
    • staticメソッド
    • 電流だけ確認することができます Thread
    • 現在のスレッドの割り込みステータスをクリアします
  • Thread.isInterrupted()
    • インスタンスメソッド
    • いずれかをチェックすることができThreadますへの参照を持っています
    • DOESは明らかではない中断状態

あなたは経由で現在のスレッドへの参照を取得することができますThread.currentThread()

あなたの背景コードの中には、現在のスレッドが中断された場合には、適切なポイントで確認したいと思い、booleanフラグが設定されている、またはTaskキャンセルされました。それは持っているなら、あなたは(帰国または例外をスローすることによってのいずれか)、任意の必要なクリーンアップおよび停止実行を行うと思います。

あなたの場合にも、ThreadそのようなIOをブロックするように、いくつかの割り込み動作を待っている、それがスローされますInterruptedException中断したとき。あなたのMVCEでは、使用Thread.sleep中断可能です。あなたが言及した例外がスローされます。このメソッドをキャンセル呼び出すときを意味します。

私は上記の「クリーンアップ」と言うときに必要な任意のクリーンアップを意味する背景には、あなたがバックグラウンドスレッドではまだだからです。あなたは(そのようなUIの更新など)FXのスレッドで何かをクリーンアップする必要があるなら、あなたは使用することができますonCancelledのプロパティをService

上記のコードでは、あなたはまた、私が保護されたメソッドを使用し表示されますsucceeded()cancelled()どちらTaskServiceこれらのメソッド(だけでなく、様々なため、他提供Worker.State複数可)を、彼らは常にFXのスレッドで呼び出されます。ドキュメントを読んで、しかし、ScheduledServiceこれらの方法のうちのいくつかのスーパー実装を呼び出すする必要があります。

2 使用している場合booleanフラグがそれに必ず更新は他のスレッドに表示されます。あなたはそれをすることによってこれを行うことができvolatile、それを同期する、または使用しましたjava.util.concurrent.atomic.AtomicBoolean

おすすめ

転載: http://43.154.161.224:23101/article/api/json?id=180516&siteId=1