3

我有这样的代码:

while(isResponseArrived)
   Thread.yield();

但我真正想做的是这样的:

long startTime = System.currentTimeInMilliseconds();

while(isResponseArrived)
{
   if(isTimeoutReached(startTime))
       throw new TimeOutExcepton(); 
   Thread.yield();
}

我还不确定是否抛出异常(这对这个问题并不重要),但我想知道的是如何让它尽可能高性能,所以我不会在处理器上大吃一惊。换句话说,我怎样才能使 isTimeoutReached(long startTime) 尽可能性能友好。

我测试过:

for(int x=0; x<99999999; x++)
    System.nanoTime();

相对

for(int x=0; x<99999999; x++)
    System.currentTimeInMilliseconds();

而且差异很小,在完成时间方面不到 10%

我还考虑使用 Thread.sleep(),但我真的希望在有更新并且处理器正在等待时尽快通知用户。Thread.yield() 不会让处理器搅动,它只是一个 NOP,给予其他任何处理器优先级,直到它可以继续。

无论如何,在不限制 CPU 的情况下测试超时的最佳方法是什么?这是正确的方法吗?

4

2 回答 2

2

根据我的经验,超时是任意选择的,例如不是时间关键的。如果我选择 1000 毫秒的超时并且它需要 1001 毫秒,那么影响应该是微不足道的。对于实现超时,我建议使实现尽可能简单。

您可以使用 ScheduledExecutorService 实现超时,例如

final ScheduledExecutorService ses = Executors.newSingleThreadScheduledExecutor();

public void addTimeoutForTask(final Future future, int timeOutMS) {
    ses.schedule(new Runnable() {
        @Override
        public void run() {
            future.cancel(true);
        }
    }, timeOutMS, TimeUnit.MILLISECONDS);
}

如果您正在执行一些非阻塞操作并且您希望它超时,您可以这样做。

interface TimedPoller {
    public void poll();

    /**
     * @return is it now closed.
     */
    public boolean checkTimeout(long nowNS);
}

private final Set<TimedPoller> timedPollers = new LinkedHashSet<>();
private volatile TimedPoller[] timedPollersArray = {};

public void add(TimedPoller timedPoller) {
    synchronized (timedPollers) {
        long nowNS = System.nanoTime();
        if (!timedPoller.checkTimeout(nowNS) && timedPollers.add(timedPoller))
            timedPollersArray = timedPollers.toArray(new TimedPoller[timedPollers.size());
    }
}

public void remove(TimedPoller timedPoller) {
    synchronized (timedPollers) {
        if (timedPollers.remove(timedPoller))
            timedPollersArray = timedPollers.toArray(new TimedPoller[timedPollers.size());
    }
}

private volatile boolean running = true;

public void run() {
    while (running) {
        // check the timeout for every 1000 polls.
        for (int i = 0; i < 1000; i += timedPollersArray.length) {
            TimedPoller[] pollers = timedPollersArray;
            for (TimedPoller poller : pollers) {
                poller.poll();
            }
        }
        long nowNS = System.nanoTime();
        TimedPoller[] pollers = timedPollersArray;
        for (TimedPoller poller : pollers) {
            if (poller.checkTimeout(nowNS))
                remove(poller);
        }
    }
}

要么放弃 CPU,要么不放弃。如果你放弃 CPU,其他线程可以运行,但你会得到一个延迟,然后才能再次运行。或者你没有放弃 CPU 来提高你的响应时间,但是另一个线程无法运行。

看来您希望能够让其他东西运行,而无需放弃 CPU。这不是微不足道的,但如果做得正确,可以给你带来两者的一些好处(或者如果没有有效地完成,两者都会带来最坏的结果)

你可以做的是实现你自己的线程逻辑,前提是你有很多小任务,例如你想轮询十个你可以只使用一个 CPU 的东西。

于 2013-01-08T11:11:40.673 回答
2

我认为使用等待/通知会更有效

boolean arrived;

public synchronized void waitForResponse(long timeout) throws InterruptedException, TimeoutException {
    long t0 = System.currentTimeMillis() + timeout;
    while (!arrived) {
        long delay = System.currentTimeMillis() - t0;
        if (delay < 0) {
            throw new TimeoutException();
        }
        wait(delay);
    }
}

public synchronized void responseArrived() {
    arrived = true;
    notifyAll();
}
于 2013-01-08T11:20:34.977 回答