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をあきらめることなく、他のものを実行できるようにしたいと考えているようです。これは些細なことではありませんが、正しく実行された場合は両方の利点のいくつかを提供できます(または効率的に実行されなかった場合は両方の最悪の場合)

実行できることは、小さなタスクがたくさんある場合に独自のスレッドロジックを実装することです。たとえば、1つのCPUだけで使用できる10個の項目をポーリングする場合などです。

于 2013-01-08T11:11:40.673 に答える
2

wait / notify を使用する方が効率的だと思います

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 に答える