4

我拥有的程序可视化物理模拟(基本上)。现在,它可以工作,但可能会变得非常迟钝,我想我知道为什么 - 在事件线程上完成了太多(读取:全部)计算。

当按下“播放”按钮时,Timer会创建一个 Swing,它会定期唤醒并调用updateTime()——到目前为止一切都很好。问题在于updateTime()迭代每个与时间相关的对象,并告诉它以适当的量(无论是实际经过的时间,还是每个滴答声的任意时间单位)将自身向前传播。这些计算以及随后的 GUI 更新都在事件分派线程上。

所以,我想尽可能多地卸载这种计算,我认为SwingWorkers 是要走的路,但我不确定如何将它们用于现有代码。我不能让现有的类扩展SwingWorker,因为它们中的许多已经扩展了其他类。

到目前为止,我最好的想法是为每个与时间相关的对象创建一个接口来实现。该接口将指定 2 个方法,calcUpdateTime()并且drawUpdateTime(). 我会将他们当前的每个updateTime()方法拆分为物理计算(进入calc_)和 GUI 更新(进入draw_)。然后我将只创建一个 SwingWorker 类,它TimeDependant在构造函数中接受一个对象,它doInBackground会调用calcUpdateTime并且done会调用drawUpdateTime. 那么我可以替换所有出现的

myObj.updateTime(currentTime);

new MySwingWorker(myObj, currentTime).execute();

我想通过 SO 运行这个想法,因为它感觉不太正确,并且它希望避免重构整个项目只是为了发现我开始时有一个坏主意。MySwingWorker此外,每个刻度可能创建数十个 s 可能不是一个坏主意吗?

感谢您阅读本文。

4

2 回答 2

2

你是对的,没有必要SwingWorker.execute()为每个滴答声调用每个工人,因为你会创建和销毁很多你真的不需要的线程。

但是,使用 aSwingWorker仍然是一个好主意,因为它为您提供了一种简单的方法,可以将需要在后台运行的代码(您的 实现SwingWorker.doInBackground())与需要在 Swing 中运行以随后更新 GUI的代码分开(你的实现SwingWorker.done())。

我建议不要使用javax.swing.Timeror ,而是使用. 基本上,它可以做所有 a可以做的事情,除了它还让您有机会控制在后台工作的线程数量、如何处理在后台线程中抛出的异常等等。java.util.Timerjava.util.concurrent.ScheduledThreadPoolExecutorjava.util.Timer

于 2010-02-20T21:43:27.297 回答
0

我在使用 Swing 计时器时体验不佳(性能方面)。由于 Swing 中的所有事件都使用同一个线程,因此它似乎有很多意外延迟。

我还建议相信你对每个滴答声都有多个 swing worker 实例的直觉:这对我来说也不合适。(根据SwingWorker文档,它被设计为只运行一次,所以你不一定能安全地重复使用它)。

一个可以做你需要的计时器是java.util.Timer. 这有很多用于指定超时的选项,尽管取决于时序仿真的保真度,即使这可能不合适。(例如:如果你想让它实时运行,但你的计算实际上比实时时间长,它应该怎么办?运行慢,或者开始跳过时间步长?)

因此,由于不确切知道您的计算/绘图例程涉及什么,我暂时建议尝试使用java.util.Timer. 当它到期时(根据您的“实时乘数”,在您认为合适的任何超时时间之后),运行所有计算,然后将结果返回 EDT 线程以进行绘图(例如,通过将它们包装在SwingUtilities.invokeLater())。

当然,如果 EDT 想要引用与计算线程相同的对象,这可能会引入锁定问题。理想情况下,如果 calc 线程可以将不可变的结果传递给 EDT,它将不必引入锁。

(免责声明:以上都没有真正考虑到多个内核/处理器,除了一个用于 GUI,一个用于计算。如果您想并行化应用程序,它可能不是一个合适的解决方案)。

于 2010-02-18T10:48:43.090 回答