通过使用JavaFX中的任务数组列表执行并等待多个并行和顺序任务 [英] Execute and wait for multiple parallel and sequential Tasks by using a Arraylist of Tasks in JavaFX

查看:72
本文介绍了通过使用JavaFX中的任务数组列表执行并等待多个并行和顺序任务的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在寻找一种合适的方法来在单独的阶段显示并行运行的任务的处理时间.

I'm looking for a suitable way to display the processing time of parallel running Tasks on a separate stage.

我想执行在ArrayList中组合的不同任务-一个接一个.对于这种情况,我正在使用ThreadPool.在每个执行的列表之后,我要等到所有任务完成.仅当任务达到成功"状态时,我才想在MainThread中执行某些操作.之后,我想执行另一个任务列表,并在一个单独的阶段将它们可视化.下图显示了所需的处理顺序(取决于下面列出的源代码):在此处输入图片描述

I want to execute different tasks combined in an ArrayList - one after the other. For this case I'm using a ThreadPool. After each executed list, I want to wait until all tasks are completed. Only when the tasks have reached the status „succeeded", I want to do something in the MainThread. After that I want to execute another list of tasks and visualize them on a separate stage as well. The following figure shows the desired processing sequence (depending on the source code listed below): enter image description here

为此,我编写了MyLoader类.MyLoader类包含一个单独的Task,并在构造函数中将progress-properties与Label和Progressbar绑定:

For this purpose I have written the classes MyLoader. The MyLoader-class contains a separate Task and binds the progress-properties with a Label and a Progressbar in the constructor:

public class MyLoader {

    public Label label = null;
    public ProgressBar progressBar = null;
    public VBox vbox; 
    public Task<Integer> task = null; 
    public String name; 

    public MyLoader(String name) {
        this.name = name; 
        this.label = new Label();
        this.progressBar = new ProgressBar();
        this.vbox = new VBox(2);

        //UI-Layout for Progress
        this.vbox.getChildren().addAll(this.label, this.progressBar);
        HBox.setHgrow(this.vbox, Priority.ALWAYS);
        this.vbox.setAlignment(Pos.CENTER);
        this.progressBar.prefWidthProperty().bind(this.vbox.widthProperty().subtract(20)); 

        //Counter-Size
        Random r = new Random();
        int max = r.nextInt((100 - 50) + 1) + 50;

        //Task
        this.task = new Task<Integer>() {

            @Override
            protected Integer call() throws Exception {

                int idx = 0;

                while(idx <= max) { 

                    Thread.sleep(20); //... for long lasting processes

                    updateMessage(name+"-progress: "+idx);
                    updateProgress(idx, max);

                    idx++; 
                }
                return max;
            }

            protected void succeeded() {
                updateMessage(name+" succeeded!");
                System.out.println(name+" succeeded!");
                super.succeeded();
            }
        };

        //Bind Properties
        this.label.textProperty().bind(task.messageProperty());
        this.progressBar.progressProperty().bind(task.progressProperty());
    } 
}

在MainClass中,我将多个MyLoader实例组合到一个ArrayList中,并使用ExecutorService运行它们.为了创建新阶段,我使用静态方法progressStage(List).在ExecutorService执行各自的任务之前,将显示每个阶段.这是MainClass代码:

In the MainClass, I combine several MyLoader instances in an ArrayList and run them with an ExecutorService. To create the new stage I use the static method progressStage(List). Each Stage is shown before the ExecutorService executes the respective tasks. Here's the MainClass code:

public class MainClass extends Application{

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

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

        //Thread-Pool
        ExecutorService es = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());      

        //FirstLoaders
        List<MyLoader> firstLoaders = new ArrayList<MyLoader>();
        firstLoaders.add(new MyLoader("A"));
        firstLoaders.add(new MyLoader("B"));

        //Show 1. Stage
        Stage firstStage =  progressStage(firstLoaders);
        firstStage.show();

        //Execute firstLoaders
        for(MyLoader l1 : firstLoaders)  
            es.execute(l1.task); 


        //1) TODO: How can I wait for the completion of the first loaders and start the second loaders?

        //... doSomething1() ...

        //SecondLoaders
        List<MyLoader> secondLoaders = new ArrayList<MyLoader>();
        secondLoaders.add(new MyLoader("C"));
        secondLoaders.add(new MyLoader("D"));
        secondLoaders.add(new MyLoader("E"));

        //Show 2. Stage
        Stage secondStage =  progressStage(secondLoaders);
        secondStage.setX(firstStage.getX());
        secondStage.setY(firstStage.getY()+firstStage.getHeight());
        secondStage.show();

        for(MyLoader l2 : secondLoaders)  
            es.execute(l2.task); 


        //2) TODO How can I wait for the completion of the second loaders and start the primaryStage?

        //... doSomething2() ...

        Scene scene = new Scene(new StackPane(), 450, 250);
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    static Stage progressStage(List<MyLoader> loaderTasks) {
        int count = loaderTasks.size();
        VBox loadBox = new VBox(count);

        for(int i=0; i<count; i++)  
            loadBox.getChildren().add(loaderTasks.get(i).vbox);  


        HBox.setHgrow(loadBox, Priority.ALWAYS);
        loadBox.setAlignment(Pos.CENTER);

        Stage dialogStage = new Stage(); 
        dialogStage.setScene(new Scene(loadBox, 300, count * 50));
        dialogStage.setAlwaysOnTop(true);
        return dialogStage; 
    } 
}

该程序到目前为止是可执行的-但计算顺序看起来完全并行.

The program is executable so far - but the calculation sequence appears completely parallel.

我的口味:

What I tasted:

1)到目前为止,我已经设法使用get()方法读取并停止了该进程.但是,只有在后台线程完成工作时才显示该阶段.

1) So far I have managed to get the process to be read and stopped using the get() method. But then the stage is only displayed when the threads in the background have finished their work.

//1) TODO: „doSomeThing1()"
List<Integer> integers = new ArrayList<Integer>(); 

for(MyLoader ml : firstLoaders)
    integers.add(ml.task.get());

System.out.println(integers.toString());

2)同样,使用Task.setOnSucceded()方法,我还没有得到任何有用的结果.主要是因为该阶段仅在计算之后显示.问题是我无法在定义的时间查询所有任务的状态.

2) Also with the Task.setOnSucceded() method I could not get any useful results yet. Mainly because the stage is only shown after the computing. The problem is that I am not able to query the status of all tasks at a defined time.

3)CountDownLatch的应用也取得了可比的结果.

3) The application of a CountDownLatch has also achieved a comparable result.

4)另外,ExecutorService的shutdown()方法导致终止.因此,该解决方案也不适合.

4) In addition, the shutdown() method of the ExecutorService causes a termination. This solution is therefore also not suitable.

//1) TODO: „doSomeThing1()"
es.shutdown();
try {
    es.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS); 

    //SecondLoaders
    //...

}catch (InterruptedException e) {
    e.printStackTrace();
}

是否有适合这种意图的方法?到目前为止,我还没有得到任何有用的结果.

Is there a suitable approach for such intentions? So far I have not come to any useful result.

推荐答案

任务完成后,只需更新一个计数器并检查当前完成的任务是否是当前集中的最后一个任务即可.

When a task is done, just update a counter and check, if the task currently completed was the last one in the current set.

以下代码演示了这一点.(虽然代码中肯定有一些地方可以改进,但是概念应该明确.)

The following code demonstrates this. (There are certainly things in the code that could be improved though, but the concept should get clear.)

public class App extends Application {

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

    private VBox taskViewContainer;
    ExecutorService executor;

    int tasksDone;
    private void runTasks(List<MyTask> tasks, IntegerProperty index) {
        if (tasks.isEmpty()) {
            index.set(index.get()+1);
        } else {
            int taskCount = tasks.size();
            tasksDone = 0;
            for (MyTask task : tasks) {
                taskViewContainer.getChildren().add(new TaskView(task));
                task.setOnSucceeded(evt -> {
                    ++tasksDone;
                    if (tasksDone == taskCount) {
                        // proceed to next task set after all tasks are done
                        index.set(index.get() + 1);
                    }
                });

                executor.submit(task);
            }
        }
    }

    @Override
    public void init() throws Exception {
        // create executor during initialisation
        executor = Executors.newFixedThreadPool(4);
    }

    @Override
    public void stop() throws Exception {
        // shutdown executor when javafx shuts down
        executor.shutdownNow();
    }

    @Override
    public void start(Stage primaryStage) throws Exception {
        taskViewContainer = new VBox();
        Label text = new Label();

        // generate random set of tasks
        Random random = new Random();
        List<List<MyTask>> taskLists = new ArrayList<>();
        for (int i = 0; i < 20; ++i) {
            int count = random.nextInt(10) + 1;
            List<MyTask> tasks = new ArrayList<>(count);
            taskLists.add(tasks);
            for (int j = 0; j < count; ++j) {
                tasks.add(new MyTask(String.format("%d.%c", i+1, (char) ('A'+j)), random.nextInt((100 - 50) + 1) + 50));
            }
        }

        // property holding the current index in the task set list
        IntegerProperty index = new SimpleIntegerProperty(-1);
        index.addListener((o, oldValue, newValue) -> {
            // gui update for change of task set
            taskViewContainer.getChildren().clear();
            text.setText(String.format("Task set %d / %d done", newValue, taskLists.size()));
            int i = newValue.intValue();
            if (i < taskLists.size()) {
                // launch next set of tasks
                runTasks(taskLists.get(i), index);
            }
        });

        // start initial tasks
        index.set(0);

        text.setMinWidth(200);
        text.setMaxWidth(Double.MAX_VALUE);
        HBox root = new HBox(text, taskViewContainer);

        root.setMinHeight(10 * 50);

        Scene scene = new Scene(root);
        primaryStage.setScene(scene);
        primaryStage.show();
    }

}

class TaskView extends HBox {

    TaskView(MyTask task) {
        setPrefSize(400, 50);
        ProgressBar progress = new ProgressBar();
        progress.progressProperty().bind(task.progressProperty());
        Label label = new Label(task.getName());
        Label message = new Label();
        message.textProperty().bind(task.messageProperty());
        getChildren().addAll(progress, new VBox(label, message));
    }

}

class MyTask extends Task<Integer> {

    private final int max;
    private final String name;

    public String getName() {
        return name;
    }

    public MyTask(String name, int max) {
        this.max = max;
        this.name = name;
    }

    @Override
    protected Integer call() throws Exception {
        int idx = 0;

        while(idx <= max) { 

            Thread.sleep(20); //... for long lasting processes

            updateMessage(name+"-progress: "+idx);
            updateProgress(idx, max);

            idx++; 
        }
        return max;
    }

}

上面的代码不具有取消任务/以异常终止的任务的可能性.

The above code does not take the possibility of canceling tasks/tasks terminating with an exception.

这篇关于通过使用JavaFX中的任务数组列表执行并等待多个并行和顺序任务的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆