gpt4 book ai didi

java - 对于 JavaFX Canvas 多线程,我应该使用什么方法?

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

我正在编写一个 JavaFX 应用程序,它在套接字上接收数据点并实时可视化它们。问题是 JavaFX 渲染太慢了。我有一个运行速度足够快的 Swing 实现,但我需要改用 JavaFX。

我工作的限制是:

  1. 可视化控件只能由 JavaFX 应用程序线程更新(我相信这是所有 JavaFX 和 Swing 应用程序所必需的)。
  2. 可视化应从人眼的角度顺利更新。每秒大约 10 次更新就足够了。每秒一次是不够的。
  3. 传入数据速率足够高(大约每秒 50 个事件,这在其他上下文中并不高)并且每个事件处理的开销足够大,以至于必须在 JavaFX 应用程序以外的线程中接收和处理传入数据线程,这样 GUI 就不会阻塞(我相信这对许多 GUI 应用程序来说是一个有点普遍的要求)。

到目前为止,我的方法是使用 Canvas JavaFX 节点作为可视化控件,并让接收线程安排对 Canvas 的更新,以便稍后在 JavaFX 应用程序线程中运行,就像这样。

    public void onEvent(Event event) {
....do processing...
Platform.runLater(new Runnable() {
@Override
public void run() {
graphics.setFill(...);
graphics.fillRect(...);
}});
}

我想到了一些可以加快速度的方法:

  1. 使用 WritableImage 而不是 Canvas 进行可视化。美中不足的是WritableImage/PixelWriter似乎没有太多的绘图方法,比如连fillRect都没有。我认为我必须实现自己的版本,而且我的版本可能会更慢。
  2. 拥有处理传入数据的线程所拥有的 Canvas 对象。从该 Canvas 复制到 Canvas ,该 Canvas 是 JavaFX 应用程序线程中场景图中的一个节点。复制可能会使用这些行的代码完成 sceneCanvas.getGraphicsContext2D().drawImage(processingCanvas.snapshot(SnapshotParameters(), null) 0, 0);。这样做的缺点是我认为它不是线程安全的,而且快照调用似乎相对昂贵。
  3. 在处理传入数据的线程中呈现 AWT BufferedImage,然后使用 SwingFXUtils.toFXImage() 从 BufferedImage 复制到 Canvas。这样做的缺点是线程语义似乎不清楚,使用 AWT 似乎有点傻。

您能否提出一些可能的方法?

谢谢!

最佳答案

我认为,主要问题是您的代码将太多绘图任务推送到 FX 应用程序线程的队列中。通常,每秒有 60 次绘图操作就足够了,这等于您的显示器的刷新率。如果你得到比这更多的“传入数据”事件,你会比必要的更频繁地绘制,浪费 CPU。因此,您必须将数据处理与绘画分离。

一种解决方案是使用 AnimationTimer。它的handle 方法会在每个动画帧中被调用,所以通常每秒调用 60 次。如果处理了新数据,动画计时器会处理重绘。

// generic task that redraws the canvas when new data arrives
// (but not more often than 60 times per second).
public abstract class CanvasRedrawTask<T> extends AnimationTimer {
private final AtomicReference<T> data = new AtomicReference<T>(null);
private final Canvas canvas;

public CanvasRedrawTask(Canvas canvas) {
this.canvas = canvas;
}

public void requestRedraw(T dataToDraw) {
data.set(dataToDraw);
start(); // in case, not already started
}

public void handle(long now) {
// check if new data is available
T dataToDraw = data.getAndSet(null);
if (dataToDraw != null) {
redraw(canvas.getGraphicsContext2D(), dataToDraw);
}
}

protected abstract void redraw(GraphicsContext context, T data);
}

// somewhere else in your concrete canvas implementation
private final RedrawTask<MyData> task = new RedrawTask<MyData>(this) {
void redraw(GraphicsContext context, MyData data) {
// TODO: redraw canvas using context and data
}
}

// may be called by a different thread
public void onDataReceived(...) {
// process data / prepare for redraw task
// ...

// handover data to redraw task
task.requestRedraw(dataToDraw);
}

关于java - 对于 JavaFX Canvas 多线程,我应该使用什么方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26082732/

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