如何在Android中减少Thread.sleep()的延迟

4
我正在一个非UI线程中循环生成定时事件,并且需要这些事件在精确的时间间隔内发生(在此,精确指变化不超过+/-5毫秒)。用户可以感知+/-10毫秒(特别是+/-20毫秒)的任何误差。在循环顶部,我做一些计算,这需要不同的时间量,但在循环底部,我需要事件在预先计算的时间发生。 以下是我尝试创建非UI线程的高度简化版本(没有异常处理)。
public final void run() {

    long loopTime = 2500L;
    long eventTime = (System.nanoTime() / 100000L) + loopTime;

    while (true) {

        calcutionsTakingVaryingAmountOfTime(); // takes 200 millisecs or less

        long eventWait = eventTime - (System.nanoTime() / 100000L);
        Thread.sleep(eventWait / 10L);
        listener.onEvent();

        eventTime = eventTime + loopTime;
    }
}

需要精确计时的是对 listener.onEvent() 的调用。

在上面的示例中,时间变量 loopTimeeventTimeeventWait 以毫秒的十分之一为单位测量时间。同样以毫秒的十分之一为单位测量当前时间的表达式 (System.nanoTime() / 100000L)

我非常确定 calcutionsTakingVaryingAmountOfTime() 总是少于 200 毫秒,并且对 listener.onEvent() 的调用只需要几毫秒。因此,如果将 loopTime 设置为 2500L,那么我的事件应该每 250 毫秒发生一次。

我已经在我的代码中添加了仪器(未显示),以便将 Thread.sleep() 的唤醒时间延迟打印到 Log.d() 中。也就是说,我计算的是

long latency = (System.nanoTime() / 100000L) - eventTime

在从Thread.sleep()返回后立即将其打印到Log.d()中。

当我在模拟器中运行时,我发现延迟(除以10后得到毫秒数)在连续通过循环时通常会在1到50毫秒之间跳动,偶尔会高达半秒钟。在实际设备上运行时,情况好了很多,但仍然有点不稳定(而且即使在模拟器上的表现也让我想知道是否会在用户设备上出现这种情况)。

为了尝试稳定我的事件并控制延迟,我尝试了几种其他方法:

  • 用调用this.wait(eventWait / 10L)代替了Thread.sleep(eventWait / 10L)的方式(我知道这是一个完全不恰当的wait()使用方法)

  • 在进入循环之前,我操纵了线程优先级,像在android库中所做的那样,调用Process.setThreadPriority(Process.THREAD_PRIORITY_URGENT_AUDIO)

但是这些都没有改善延迟。

唯一一个稳定事件,并将延迟降低到少于2到3毫秒,并且很少有抽搐的方法是通过替换Thread.sleep()调用为轮询循环:

while ((System.nanoTime() / 100000L) < eventTime)
    ;

一方面,我觉得像一个醉汉一样浪费机器周期很尴尬。另一方面,我开始认为没有比在轮询循环中消耗机器周期更好的方法,我应该在这里烧掉机器周期以减少延迟并满足我的规格要求。当然,当我的应用程序进入后台时,我会暂停线程,因此这个轮询循环可以工作。但真是太浪费了。

如果有任何想法,将不胜感激。

1个回答

2

我在类似的情况下使用了Handler来延迟消息发送。但这可能有些过头了。在你的情况下,我建议看一下Timer类。

mTimer = new Timer();
mTimer.scheduleAtFixedRate(new TimerTask() {
    @Override
    public void run() {
        Log.v("TEST", " tick");
    }
}, 0, 250);

这使我在模拟器上的延迟为+-2毫秒。


谢谢您的建议。我尝试了几件事情。首先,我尝试使用调用“java.util.Timer.schedule(TimerTask task,long delay)”作为我的调用“Thread.sleep()”的替代品。它大致是这样的: - singerofwords
谢谢您的建议。我尝试了几件事情。首先,我尝试使用调用'java.util.Timer.schedule(TimerTask task, long delay)'作为我的调用'Thread.sleep()'的替代品。然后我尝试使用像你的'scheduleAtFixedRate()'。在这两种情况下,模拟器上的延迟从1到70毫秒不等,虽然在设备上延迟几乎符合规格。您是否还有其他方法可以在模拟器上获得+/-2毫秒的延迟? - singerofwords
我只运行了一个小的测试应用程序,所以几乎没有任何压力。我的CPU是英特尔(R)Core(TM)2 Duo CPU T9300 @ 2.50GHz。 - Salw
谢谢回复,对我很有用。 - singerofwords
@singerofwords:我和你有完全相同的问题。 Thread.sleep() 破坏了我的应用程序,因为我也有像你描述的那样大的延迟。但是,你如何使用 Timer 模拟 Thread.sleep?你已经找到更好的解决方案了吗? - Luis A. Florit

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接