最近我做了一个非常基本的程序。一种具有暂停功能的计时器。它使用3个线程,2个用于Swing,1个用于主线程。
对于这个程序,应该在主线程中有一个增量时间计数部分。我制定了一个非常基本的系统;
while(true)
{
long now = System.currentTimeMillis();
if(!sessionPaused)
{
if(now-programLastMs>1000)
{
save();
programLastMs = now;
}
sessionMs += now-sessionPrevMs;
overallMs += now-sessionPrevMs;
sessionPrevMs = now;
sessionLabel.setText(formatMillis("This Session:<br/>",sessionMs));
overallLabel.setText(formatMillis("Overall:<br/>", overallMs));
}
}
上面的代码导致CPU使用率高。然后我将该代码块替换为:
timer.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
long now = System.currentTimeMillis();
if(!sessionPaused)
{
if(now-programLastMs>1000)
{
save();
programLastMs = now;
}
sessionMs += now-sessionPrevMs;
overallMs += now-sessionPrevMs;
sessionPrevMs = now;
sessionLabel.setText(formatMillis("This Session:<br/>",sessionMs));
overallLabel.setText(formatMillis("Overall:<br/>", overallMs));
}
}
}, 0, 1);
问题已经过去了。我只是想知道为什么。此外,创建程序循环的最佳方式是什么?
您的第一个代码块基本上以CPU的速度运行。要理解这一点,您需要了解IPS和FLOPS是什么。一个现代的CPU会执行一些GFLOPS,这意味着你的第一个块每秒会被执行几十甚至几十万次,这取决于你的硬件。如果你在主线程上运行它,它会阻塞你的UI(即你的GUI会被卡住)。在一个单独的线程上,它将持续运行,占用资源,而实际上没有做太多工作。
另一方面,第二个块具有每毫秒执行一次的特定指令。这意味着您的代码块被执行1000次,其余的cpu时间被释放用于其他工作。
为了解决您评论中的问题:使用无限循环几乎是不可接受的。循环最适合用于一组指令的有界重复。在您的示例中,您似乎希望每1000ms调用save,同时计算会话时间。我会
- 将保存分离到一个重复计时器,该计时器每1000ms调用一次
- 创建一个onSessionPause和onSessionResume,仅当暂停状态更改时。这可能是由于暂停按钮或您定义的某种其他条件造成的。onSessionPause和onSessionResume函数应该能够记录调用它们的时间,并根据时间差更新sessionMs
除非你期望每毫秒都会发生变化,否则你会让CPU做不必要的工作。
此外,创建程序循环的最佳方式是什么?
在您的特定情况下,最佳方式是每秒运行一次循环,因为这将以最低的CPU使用率实现您的目标。由于这是不可能保证的,您需要找到一个足够低的阈值,因此您的循环至少每秒执行一次,并且不要太低,以提高效率。我建议你用一些输出进行测试。
不过要小心,不同的代码有不同的要求,所以没有通用的最佳方式来循环代码。作为一条基本规则,您应该尽量避免重复执行。