【问题标题】:Is there a solution for an ExecutorService that time limits each submitted Callable是否有针对每个提交的 Callable 时间限制的 ExecutorService 解决方案
【发布时间】:2012-11-05 16:40:12
【问题描述】:

这就是我所拥有的...我有一个任务列表,每个任务都将单独提交ExecutorService。我希望每个任务在开始执行后都有一个 x MS 的时间限制。我已经看到了类似Time Limit on Individual Threads 上发布的解决方案,但这对提交任务时开始设置了时间限制。

我认为一种解决方案可以将已接受的解决方案扩展到Time Limit on Individual Threads。这会将Callable 包装在另一个Callable 中,一旦它开始就会将取消任务放在计划的执行程序上。这将涉及以非平凡的方式将Callable 映射到Future,所以我想我会发帖看看是否有人知道现有的解决方案(代码重用是一件很棒的事情)。

谢谢。

作为仅供参考,Time Limit on Individual Threads 发布的解决方案如下。这再次从提交时间取消,而不是开始时间:

ExecutorService service = Executors.newFixedThreadPool(N);
ScheduledExecutorService canceller = Executors.newSingleThreadScheduledExecutor();

public <T> Future<T> executeTask(Callable<T> c, long timeoutMS){
   final Future<T> future = service.submit(c);
   canceller.schedule(new Callable<Void>(){
       public Void call(){
          future.cancel(true);
          return null;
       }
    }, timeoutMS, TimeUnit.MILLI_SECONDS);
   return future;
}

【问题讨论】:

    标签: java concurrency executorservice


    【解决方案1】:

    看看这是否适合你。

    对不起,如果代码混乱,但只是为了演示概念:将未来传递给任务,让任务启动自己的计时器 - 计时器将在任务启动时启动。

    以下代码添加了10个任务,每个任务执行时间为2秒,超时时间为1秒。

    import java.util.Timer;
    import java.util.TimerTask;
    import java.util.concurrent.Callable;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    import java.util.concurrent.Future;
    import java.util.concurrent.ScheduledExecutorService;
    import java.util.concurrent.TimeUnit;
    
    public class CancellableTaskDemo {
        public static void main(final String[] args) {
            new CancellableTaskDemo();
        }
    
        final ExecutorService ex = Executors.newFixedThreadPool(3);
    
        public CancellableTaskDemo() {
            for (int i = 0; i < 10; i++) {
                final int c = i;
                submitTask(new Callable<Object>() {
                    @Override
                    public Object call() throws Exception {
                        final long t = System.currentTimeMillis();
                        try {
                            Thread.sleep(2000);
                            System.out.println("Task " + c + " done in " + (System.currentTimeMillis() - t) + "ms");
                        } catch (final InterruptedException e) {
                            System.out.println("Task " + c + " aborted after " + (System.currentTimeMillis() - t) + "ms");
                        }
                        return null;
                    }
                }, 1000);
            }
            ex.shutdown();
            try {
                ex.awaitTermination(100000, TimeUnit.MILLISECONDS);
            } catch (final InterruptedException e) {
                e.printStackTrace();
            }
        }
    
        private void submitTask(final Callable<Object> c, final int timeout) {
            final TimedFutureTask tft = new TimedFutureTask(c, timeout);
            final Future<?> ft = ex.submit(tft.getCallable());
            tft.setFuture(ft);
        }
    
        static class TimedFutureTask {
            final static ScheduledExecutorService canceller = Executors.newSingleThreadScheduledExecutor();
            private Timer cancelTimer;
            private Callable<Object> timedCallable;
            private Future<?> f;
            private int timeoutMS;
    
            public TimedFutureTask(final Callable<Object> callable, final int timeoutMS) {
                this.timeoutMS = timeoutMS;
                timedCallable = (new Callable<Object>() {
    
                    @Override
                    public Object call() throws Exception {
    
                        cancelTimer = new Timer();
                        cancelTimer.schedule(new TimerTask() {
    
                            @Override
                            public void run() {
                                f.cancel(true);
    
                            }
                        }, timeoutMS);
    
                        final Object res = callable.call();
                        cancelTimer.cancel();
                        return res;
                    }
    
                });
            }
    
            public Callable<Object> getCallable() {
                return timedCallable;
            }
    
            public void setFuture(final Future<?> future) {
                f = future;
            }
        }
    
    }
    

    【讨论】:

    • +1 这是我认为的解决方案的一个很好的示例实现。这可能是我使用的。不过,发帖的目的是看看有没有现成的库/JDK机制来做这件事。
    • 我在 SourceForge.net 上维护了几个开源项目。其中之一可能是您正在寻找的。本文是关于尴尬的并行项目:Fork-Join Development in Java SE
    【解决方案2】:

    我会使用 ScheduleExecutorService。

    final Future future = es.submit(myTask);
    ses.schedule(new Runnable() {
        public void run() {
            future.cancel(true);
        }
    }, timeout, timeUnit);
    

    【讨论】:

    • 正如我在帖子中所说,此解决方案从提交时取消任务timeout,而不是从开始执行时取消timeout
    • 问题中的链接仅指您的相同答案:)
    • AFAIK,没有简单的方法可以做到这一点,因为 Callable 可以在 Future 返回之前启动。您可以将其包装在 Callable 中,假设一旦运行,它就会在不久之后运行。
    【解决方案3】:

    你可以手动完成,但它的代码太多,做一个简单的任务:(

    public void run() {
        long start = System.nanoTime();
        long timeout = 60 * 1000;
        do {
            // Do work here
        } while (TimeUnit.MICROSECONDS.convert(System.nanoTime() - start,
                TimeUnit.MICROSECONDS)
                - start > timeout);
    }
    

    【讨论】:

      【解决方案4】:

      您可以将计时器作为您提交的任务的一部分启动,因此它会在任务执行时启动。只是,在这种情况下,您不能使用future.cancel,但可以中断线程。根据您提交的任务,是否可以轻松地及时终止。

      【讨论】:

      • 这意味着 Callable 必须自己实现它并在整个代码中检查计时器。
      • 您可以在提交周围构建一个包装器以自动嵌入代码,我稍后会发布一个示例
      • 这会阻止提交调用,阻止调用者提交多个可调用对象并等待它们全部完成。
      猜你喜欢
      • 2013-06-29
      • 1970-01-01
      • 2021-08-16
      • 2019-12-16
      • 2011-01-19
      • 1970-01-01
      • 2011-06-16
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多