0

如何在特定时间内运行线程并在时间过去时返回一些结果?

到目前为止,我能想到的最好的解决方案是手动测量时间。但也许有更优雅、开箱即用的解决方案? 

我有一个算法,可以在每次迭代中改进以前的解决方案。我想在预定义的时间内在单独的线程中运行此代码。当时间过去时,应该返回最好的(最新的)解决方案。

由于我想返回解决方案,我不能只使用Future#get(long timeout, TimeUnit unit)- 它会导致TimeoutException. 在“控制”线程一段时间后中断线程也是如此 - 在这种情况下,Future将被取消并返回null

我目前的解决方案如下:

定时器逻辑:

private class ExecutionTimer {

    private final long executionTimeLimit;

    private long startTime;

    // accepts execution time limit in _miliseconds_
    public ExecutionTimer(final int executionTimeLimit) {
        this.executionTimeLimit = TimeUnit.MILLISECONDS.toNanos(executionTimeLimit);
    }

    public void start() {
        this.startTime = System.nanoTime();
    }

    public boolean hasElapsed() {
        return (System.nanoTime() - startTime) >= executionTimeLimit;
    }
}

...和工作线程:

 private class WorkerThread implements Callable<Double> {

        private final ExecutionTimer executionTimer;

        public WorkerThread(final int executionTimeLimit) {
            this.executionTimer = new ExecutionTimer(executionTimeLimit);
        }

        @Override
        public Double call() throws Exception {
            executionTimer.start();

            double partialSolution = 0;
            while (!executionTimer.hasElapsed()) {
                // let's imagine that here solution is improved ;)
                partialSolution = new Random().nextDouble(); 
            }
            return partialSolution;
        }
    }

编辑: 工作线程可以无限期地工作而不会从外部中断它 - 这很好,因为算法总是可以改进以前的解决方案(当然经过一些显着的时间改进相对较小)

4

4 回答 4

2

您可以将中间结果存储在共享线程安全变量中(例如volatile double在您的情况下) - 当您的未来超时时,您可以从该变量中检索最新的计算值。

换句话说:

  • 如果future.get(...)返回一个值,则使用它
  • 如果您得到 a TimeoutException,则通过调用 检索该值,该值yourWorkerThread.getLatestValue();返回volatile double latestValue在每个循环中更新的 a ,而不是您的 local partialSolution

或者,这篇文章指向 Guava 库和其他解决方案(所有这些都归结为我评论中讨论的 2 个选项)。请注意,Guava 在内部使用带有超时的未来。

于 2012-08-18T17:56:12.457 回答
1

考虑使用Timer & TimerTask

import java.util.TimerTask;
import java.util.concurrent.Callable;
import java.util.concurrent.atomic.AtomicBoolean;

public class Solver implements Callable<Double> {

    private final AtomicBoolean running = new AtomicBoolean(true);

    public void stop() {
        this.running.set(true);
    }

    @Override
    public Double call() throws Exception {
        Double answer = null;
        while (this.running.get()) {
            answer = keepImproving();
        }
        // TODO Auto-generated method stub
        return answer;
    }

}

class Schedular extends TimerTask {

    private final Solver solver;

    public Schedular(Solver solver) {
        this.solver = solver;
    }

    @Override
    public void run() {
        this.solver.stop();
    }

}

使用类似下面的东西

final Solver solver = new Solver();
Schedular schedular = new Schedular(solver);
final Timer timer = new Timer();
timer.schedule(schedular, 0, TimeUnit.MINUTES.toMillis(1));

ExecutorService executor = // get a executor somehow
final Future<Double> future = executor.submit(solver);
final Double answer = future.get();
System.out.println(answer);

这个想法是使用Timer&TimerTask触发停止信号,以便算法停止改进答案。

于 2012-08-18T19:35:06.853 回答
1

我建议使用生产者-消费者模式:

负责培养结果的算法不断产生新的更好的结果,并将它们放入共享线程安全资源中。

对此结果感兴趣的客户端会在每个预定义的时间间隔使用此资源,在您的问题中设置为超时。

资源本身可以是一个流(例如 BlockingQueue)或单个变量。

这具有很容易推理的优点,它定义了清晰的边界并且非常灵活。例如:客户端只要完全没有结果就可以阻塞,或者只要没有新的和改进的结果就可以阻塞。生产者消费者模式的所有变体,只需调整生产者通知条件。

于 2012-08-18T19:18:55.943 回答
0

您可以使用普通的 Future.get(),它会无限期地等待。

于 2012-08-18T17:57:58.243 回答