JavaFX 2:后台和Platform.runLater与任务/服务 [英] JavaFX 2: background and Platform.runLater vs Task/Service

查看:844
本文介绍了JavaFX 2:后台和Platform.runLater与任务/服务的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我对JavaFX中的任务 / 服务的概念感到困惑。

I'm pretty confused about the concept of Task/Service in JavaFX.

我使用了一个基于后台线程的模型用于我的后台工作,它调用 Platform.runLater 更新到UI。

I have used a model based on a background thread for my background work, which call Platform.runLater for any update to the UI.

假设我对进度条不感兴趣。我在我的模型上做一些真正的工作,必须在GUI的视图中更新(例如,基于一些连接在时间上更新的参与者的列表,基于一些用户输入的参与者列表,根据年龄分类的参与者列表和原产地)。这是我通常用背景线程实现的,我开始,其中我使用 Platform.runLater

Let's say I'm not interested in a progress bar or such. I'm doing some real work on my model that must be updated in the view of the GUI (e.g a list of participants which updates over time based on some connection in the background, list of participant based on some user input, classified by age and origin). This is what I usually achieve with background threads that I start, and within which I use Platform.runLater.

现在在JavaFX 2中,他们使用任务 s和 code> s,表明最好使用它们。但我没有看到任何实现我正在谈论的例子。

Now in JavaFX 2 they have all this concurrency using Tasks and Services, suggesting that it is better to use them. But I don't see any examples that achieve what I'm talking about.

通过绑定一些属性更新进度条是很好的(但这些都是有关任务的信息不是你的模型)。

Updating the progress bar by binding some properties is nice (but those are information on the task not your model).

那么,我如何根据我的模型更新我的意见的内容呢?我应该从任务中调用 Platform.runLater 吗?如果没有,机制是什么?如何捕获任务成功并获得结果(更新实际模型)以更新视图?

So, how can I actually update the content of my views based on my model? Should I call Platform.runLater from within the Task? If not, what is the mechanism? How do I catch when the tasks have succeed and get the result (the update of the actual model) to update the view?

Oracle的教程不幸的是不是很好在这方面。

The tutorials by Oracle unfortunately were not very good in this regard. Pointing me to some good tutorials would also help.

推荐答案

任务服务类旨在鼓励在GUI编程中的一些(但不是所有)常见场景的良好实践和正确使用并发。

The Task and Service classes are designed to encourage good practice and proper use of concurrency for some (but not all) common scenarios in GUI programming.

典型的情况是,应用程序需要执行一些逻辑来响应用户操作,这可能需要很长时间(可能需要很长的计算,数据库查找)。该过程将返回一个结果,然后用于更新UI。如你所知,长时间运行的进程需要在后台线程上执行以保持UI响应,并且UI的更新必须在FX应用程序线程上执行。

A typical scenario is that the application needs to execute some logic in response to a user action which may take a long time (maybe a long calculation, or, more commonly, a database lookup). The process will return a result which is then used to update the UI. As you know, the long-running process needs to be executed on a background thread to keep the UI responsive, and the update to the UI must be executed on the FX Application Thread.

Task 类提供了这种功能的抽象,代表一个一次性被执行并产生结果。 call()方法将在后台线程上执行,并被设计为返回进程的结果,并且有任务完成时通知的事件侦听器在FX应用程序线程。强烈建议开发人员使用immutable状态初始化 Task 实现,并使 call()方法返回一个不可变对象,这保证了后台线程和FX应用程序线程之间的正确同步。

The Task class provides an abstraction for this kind of functionality, and represents a "one-off" task that is executed and produces a result. The call() method will be executed on the background thread, and is designed to return the result of the process, and there are event listeners for when the task completes that are notified on the FX Application thread. The developer is strongly encouraged to initialize the Task implementation with immutable state and have the call() method return an immutable object, which guarantees proper synchronization between the background thread and the FX Application Thread.

对这些任务有额外的共同要求,例如更新消息或进度任务进行。应用程序还可能需要监视类的生命周期状态(等待运行,运行,完成,异常失败等)。正确编程是相当困难的,因为它必然涉及在两个不同的线程中访问可变状态,有许多应用程序开发人员不知道微妙。 Task 类为这种功能提供了简单的钩子,并负责所有的同步。

There are additional common requirements on these kinds of tasks, such as updating a message or the progress as the task progresses. The application may also need to monitor the life-cycle state of the class (waiting to run, running, completed, failed with an exception, etc). Programming this correctly is quite subtly difficult, as it necessarily involves accessing mutable state in two different threads, and there are many application developers who are unaware of the subtleties. The Task class provides simple hooks for this kind of functionality and takes care of all the synchronization.

要使用功能,只需创建任务,其 call()方法返回计算结果,注册一个处理程序状态从 RUNNING 转换到 SUCCEEDED ,并在后台线程中运行该任务:

To use this functionality, just create a Task whose call() method returns the result of your computation, register a handler for when the state transitions from RUNNING to SUCCEEDED, and run the task in a background thread:

final Task<MyDataType> task = new Task<MyDataType>() {
    @Override
    public MyDataType call() throws Exception {
        // do work here...
        return result ;
    }
};

task.setOnSucceeded(new EventHandler<WorkerStateEvent>() {
    @Override
    public void handle(WorkerStateEvent event) {
        MyDataType result = task.getValue(); // result of computation
        // update UI with result
    }
});

Thread t = new Thread(task);
t.setDaemon(true); // thread will not prevent application shutdown
t.start();

在后台工作的方式是 Task 维护一个状态属性,它使用一个常规的JavaFX ObjectProperty 实现。 任务本身包裹在 Callable 的私有实现中, Callable implementation是传递给超类构造函数的对象。因此, Callable call()方法实际上是在后台线程中执行的方法。 Callable call()方法实现如下:

The way this works behind the scenes is that the Task maintains a state property, which is implemented using a regular JavaFX ObjectProperty. The Task itself is wrapped in a private implementation of Callable, and the Callable implementation is the object passed to the superclass constructor. Consequently, the Callable's call() method is actually the method executed in the background thread. The Callable's call() method is implemented as follows:


  1. 在FX应用程序线程上调用一次调用(即使用 Platform.runLater())更新状态,先到 SCHEDULED ,然后到 RUNNING

  2. 调用任务调用()方法(即用户开发的 call()方法)

  3. 在FX应用程序线程上安排一个更新 value call()方法的结果

  4. 在FX应用程序线程上调用更新状态属性 SUCCEEDED

  1. Schedule a call on the FX Application thread (i.e. using Platform.runLater()) that updates the state, first to SCHEDULED, then to RUNNING
  2. Invoke the call() method of the Task (i.e. the user-developed call() method)
  3. Schedule a call on the FX Application Thread that updates the value property to the result of the call() method
  4. Schedule a call on the FX Application Thread that updates the state property to SUCCEEDED

当然调用用 state 属性注册的监听器,并且由于在FX应用程序线程上调用了状态更改,所以监听器的句柄)方法。

This last step will of course invoke listeners registered with the state property, and since the state change was invoked on the FX Application Thread, so to will those listeners' handle() methods.

有关如何工作的完整理解,请参阅源代码

For a full understanding of how this works, see the source code.

通常,应用程序可能需要在多个离散时间执行这些任务,并监视代表所有进程的当前状态(即running现在表示一个实例正在运行,等等)。 Service 类通过 createTask()方法提供了一个包装器。当 Service 开始时,通过调用 createTask()获得 Task code>,通过 Executor 执行它,并相应地转换自己的状态。

Commonly, the application may want to execute these tasks multiple discrete times, and monitor the current state representing all of the processes (i.e. "running" now means one instance is running, etc). The Service class simply provides a wrapper for this via a createTask() method. When the Service is started, it gets a Task instance by calling createTask(), executes it via its Executor, and transitions its own state accordingly.

当然有许多并发用例不适合(至少干净)到 Task Service 实现。如果你有一个单一的背景 Thread 在你的应用程序的整个持续时间运行(所以它代表一个连续的过程,而不是一次性任务),然后任务类不是一个很好的适合。这样的例子可能包括游戏循环,或(可能)轮询。在这些情况下,你可以更好地使用自己的线程 Platform.runLater()更新UI,但是当然你必须处理两个线程可以访问的任何变量的正确同步。根据我的经验,值得花费一些时间考虑这些要求是否可以重新组织成适合任务服务模型,如果这样可以完成,结果代码结构通常更加干净,更容易管理。但是,在某些情况下不是这种情况,在这种情况下使用线程 Platform.runLater()是适当的。

There are of course many concurrency use cases that don't fit (at least cleanly) into the Task or Service implementations. If you have a single background Thread that is running for the entire duration of your application (so it represents a continuous process, rather than a one-off task), then the Task class is not a good fit. Examples of this might include a game loop, or (perhaps) polling. In these cases you may well be better off using your own Thread with Platform.runLater() to update the UI, but of course you have to handle proper synchronization of any variables that may be accessed by both threads. In my experience, it is worth spending some time thinking about whether these requirements can be re-organized into something that does fit into the Task or Service model, as if this can be done the resulting code structure is often much cleaner and easier to manage. There are certainly cases where this is not the case, however, in which case using a Thread and Platform.runLater() is appropriate.

对轮询的最后一个注释(或周期性排定的后台任务的任何其他要求)。 Service 类似乎是一个很好的候选人,但事实证明是很难有效地管理周期性。 JavaFX 8引入了一个 ScheduledService 类,它非常好地处理这个功能,并且还增加了对后台任务重复失败等情况的处理。

One last comment on polling (or any other requirement for a periodically-scheduled background task). The Service class looks like a good candidate for this, but it turns out to be quite hard to manage the periodicity effectively. JavaFX 8 introduced a ScheduledService class which takes care of this functionality quite nicely, and also adds handling for cases such as repeated failure of the background task.

这篇关于JavaFX 2:后台和Platform.runLater与任务/服务的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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