gpt4 book ai didi

JavaFX 2 : background and Platform. runLater vs 任务/服务

转载 作者:搜寻专家 更新时间:2023-11-01 04:04:15 24 4
gpt4 key购买 nike

我对 Task 的概念很困惑/Service在 JavaFX 中。

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

假设我对进度条等不感兴趣。我正在对我的模型做一些真正的工作,必须在 GUI 的 View 中更新(例如,根据后台的某些连接随时间更新的参与者列表,基于某些用户输入的参与者列表,按年龄分类和起源)。这就是我通常使用我启动的后台线程实现的目标,并在其中使用 Platform.runLater .

现在在 JavaFX 2 中,他们使用 Task 拥有所有这些并发性。 s 和 Service s,建议最好使用它们。但是我没有看到任何可以实现我所说的内容的示例。

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

那么,我如何才能根据我的模型实际更新 View 的内容呢?我应该打电话Platform.runLater从内部Task ?如果不是,机制是什么?我如何捕捉任务何时成功并获得结果(实际模型的更新)以更新 View ?

不幸的是,Oracle 的教程在这方面不是很好。向我指出一些好的教程也会有所帮助。

最佳答案

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

一个典型的场景是应用程序需要执行一些逻辑来响应可能需要很长时间的用户操作(可能是长时间的计算,或者更常见的是数据库查找)。该过程将返回一个结果,然后用于更新 UI。如您所知,长时间运行的进程需要在后台线程上执行以保持 UI 响应,并且对 UI 的更新必须在 FX 应用程序线程上执行。
Task类为这种功能提供了抽象,并表示执行并产生结果的“一次性”任务。 call()方法将在后台线程上执行,旨在返回进程的结果,并且在 FX 应用程序线程上通知任务完成时的事件监听器。强烈建议开发人员初始化 Task具有不可变状态的实现并具有 call()方法返回一个不可变对象(immutable对象),这保证了后台线程和 FX 应用程序线程之间的正确同步。

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

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

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维护一个 state属性,使用常规 JavaFX 实现 ObjectProperty . Task本身包含在 Callable 的私有(private)实现中,以及 Callable implementation 是传递给父类(super class)构造函数的对象。因此, Callablecall()方法实际上是在后台线程中执行的方法。 Callablecall()方法实现如下:
  • 在更新 Platform.runLater() 的 FX 应用程序线程上安排调用(即使用 state) ,先到SCHEDULED ,然后到 RUNNING
  • 调用 call() Task的方法(即用户开发的 call() 方法)
  • 在更新 value 的 FX 应用线程上安排调用call() 的结果的属性方法
  • 在更新 state 的 FX 应用线程上安排调用属性(property)到SUCCEEDED

  • 最后一步当然会调用注册到 state 的监听器。属性,并且由于在 FX 应用程序线程上调用了状态更改,因此这些监听器的 handle() 也将被调用。方法。

    要全面了解其工作原理,请参阅 source code .

    通常,应用程序可能希望在多个离散时间执行这些任务,并监视代表所有进程的当前状态(即“运行”现在意味着一个实例正在运行等)。 Service类通过 createTask() 简单地为此提供了一个包装器。方法。当 Service启动,它得到一个 Task实例通过调用 createTask() ,通过它的 Executor 执行它,并相应地转换自己的状态。

    当然,有许多并发用例不适合(至少完全不适合) TaskService实现。如果您只有一个背景 Thread在应用程序的整个持续时间内运行(因此它代表一个连续的过程,而不是一次性任务),然后是 Task类(class)不太合适。这方面的示例可能包括游戏循环或(可能)轮询。在这些情况下,您最好使用自己的 ThreadPlatform.runLater()更新 UI,但当然您必须处理两个线程可能访问的任何变量的正确同步。根据我的经验,值得花一些时间考虑是否可以将这些要求重新组织为适合 Task 的内容。或 Service模型,好像可以做到这一点,结果代码结构通常更清晰,更易于管理。然而,在某些情况下肯定不是这种情况,在这种情况下使用 ThreadPlatform.runLater()是合适的。

    关于轮询(或对定期调度的后台任务的任何其他要求)的最后一条评论。 Service class 看起来是一个很好的候选者,但事实证明很难有效地管理周期性。 JavaFX 8 引入了 ScheduledService类很好地处理了此功能,并且还添加了对后台任务重复失败等情况的处理。

    关于JavaFX 2 : background and Platform. runLater vs 任务/服务,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25224323/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com