编程语言
首页 > 编程语言> > JavaFX Canvas多线程应使用哪种方法?

JavaFX Canvas多线程应使用哪种方法?

作者:互联网

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

我正在研究的约束是:

>可视化控件只能由JavaFX应用程序线程更新(我相信这对于所有JavaFX和Swing应用程序都是必需的).
>应该从人眼的角度平滑更新可视化效果.每秒大约10次更新就足够了.每秒一次是不够的.
>传入数据速率足够高(每秒约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(...);
                }});
    }

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

>使用WritableImage而不是Canvas进行可视化.缺点是WritableImage / PixelWriter似乎没有很多绘制方法,例如,它甚至没有fillRect.我想我必须实现自己的版本,而我的版本可能会变慢.
>具有处理输入数据的线程所拥有的Canvas对象.从该画布复制到JavaFX应用程序线程中场景图中节点的画布.复制可能会通过以下代码完成:sceneCanvas.getGraphicsContext2D().drawImage(processingCanvas.snapshot(SnapshotParameters(),null)0,0);.缺点是我认为它不是线程安全的,并且快照调用似乎相对昂贵.
>渲染到处理输入数据的线程中的AWT BufferedImage,然后使用SwingFXUtils.toFXImage()从BufferedImage复制到Canvas.缺点是线程语义似乎不清楚,使用AWT似乎有点愚蠢.

您能提出一些潜在的方法吗?

谢谢!

解决方法:

我认为,主要问题是您的代码将太多的绘图任务推入FX Application线程的队列中.通常,每秒进行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);
}

标签:multithreading,javafx-2,java
来源: https://codeday.me/bug/20191121/2050083.html