【问题标题】:How can I wrap a method so that I can kill its execution if it exceeds a specified timeout?如何包装一个方法,以便在它超过指定的超时时终止它的执行?
【发布时间】:2008-10-27 15:34:02
【问题描述】:

我有一个我想调用的方法。但是,如果执行时间过长,我正在寻找一种干净、简单的方法来杀死它或强制它返回。

我正在使用 Java。

说明:

logger.info("sequentially executing all batches...");
for (TestExecutor executor : builder.getExecutors()) {
logger.info("executing batch...");
executor.execute();
}

我认为TestExecutor 类应该implement Callable 并继续朝那个方向发展。

但我想做的就是停止executor.execute(),如果它花费的时间太长。

建议...?

编辑

收到的许多建议都假定正在执行的需要很长时间的方法包含某种循环,并且可以定期检查变量。 然而,这种情况并非如此。所以一些不一定是干净的,只要可以接受就会停止执行。

【问题讨论】:

    标签: java multithreading concurrency callable


    【解决方案1】:

    你应该看看这些类: FutureTaskCallableExecutors

    这是一个例子:

    public class TimeoutExample {
        public static Object myMethod() {
            // does your thing and taking a long time to execute
            return someResult;
        }
    
        public static void main(final String[] args) {
            Callable<Object> callable = new Callable<Object>() {
                public Object call() throws Exception {
                    return myMethod();
                }
            };
            ExecutorService executorService = Executors.newCachedThreadPool();
    
            Future<Object> task = executorService.submit(callable);
            try {
                // ok, wait for 30 seconds max
                Object result = task.get(30, TimeUnit.SECONDS);
                System.out.println("Finished with result: " + result);
            } catch (ExecutionException e) {
                throw new RuntimeException(e);
            } catch (TimeoutException e) {
                System.out.println("timeout...");
            } catch (InterruptedException e) {
                System.out.println("interrupted");
            }
        }
    }
    

    【讨论】:

    • 我唯一要补充的是finally { executorService .shutdown(); } 做一些清理工作。
    【解决方案2】:

    Java 的interruption mechanism 就是针对这种情况而设计的。如果您希望中止的方法正在执行一个循环,只需让它在每次迭代时检查线程的interrupted status。如果被中断,则抛出 InterruptedException。

    然后,当你想中止时,你只需要在适当的线程上调用interrupt

    或者,您可以使用approach Sun 建议作为已弃用的停止方法的替代方法。这不涉及抛出任何异常,该方法将正常返回。

    【讨论】:

    • 很遗憾没有循环。
    【解决方案3】:

    我假设在以下语句中使用了多个线程。

    我在这方面做了一些阅读,大多数作者说杀死另一个线程是个坏主意。

    如果您要终止的函数可以设计为定期检查变量或同步原语,然后在设置了该变量或同步原语时干净地终止,那将非常干净。然后某种监视器线程可以休眠数毫秒,然后设置变量或同步原语。

    【讨论】:

    • 感谢您的回答。在这种情况下,该函数不能设计为定期检查变量或同步原语。
    【解决方案4】:

    真的,你不能...唯一的方法是使用 thread.stop,同意“合作”方法(例如,偶尔检查 Thread.isInterrupted 或调用抛出 InterruptedException 的方法,例如 Thread.sleep()),或者以某种方式完全调用另一个 JVM 中的方法。

    对于某些类型的测试,调用 stop() 是可以的,但它可能会损坏测试套件的状态,因此如果您想避免交互,则必须在每次调用 stop() 后重新启动 JVM效果。

    有关如何实施协作方法的详细说明,请查看Sun's FAQ on the deprecated Thread methods

    对于这种方法在现实生活中的示例,Eclipse RCP's Job API's'IProgressMonitor' 对象允许某些管理服务(通过“取消”方法)向子进程发出信号,表明它们应该停止。当然,这依赖于定期实际检查 isCancelled 方法的方法,而他们经常无法做到这一点。

    一种混合方法可能是用中断很好地询问线程,然后在几秒钟后用停止坚持。同样,您不应该在生产代码中使用 stop ,但在这种情况下它可能没问题,尤其是。如果您很快退出 JVM。

    为了测试这种方法,我编写了一个简单的工具,它接受一个可运行文件并尝试执行它。随意评论/编辑。

    public void testStop(Runnable r) {
        Thread t = new Thread(r);
        t.start();
        try {
            t.join(2000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    
        if (!t.isAlive()) {
            System.err.println("Finished on time.");
            return;
        }
    
        try {
            t.interrupt();
            t.join(2000);
            if (!t.isAlive()) {
                System.err.println("cooperative stop");
                return;
            }
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.err.println("non-cooperative stop");
        StackTraceElement[] trace = Thread.getAllStackTraces().get(t);
        if (null != trace) {
            Throwable temp = new Throwable();
            temp.setStackTrace(trace);
            temp.printStackTrace();
        }
        t.stop();
        System.err.println("stopped non-cooperative thread");
    }
    

    为了测试它,我编写了两个相互竞争的无限循环,一个是协作的,一个从不检查其线程的中断位。

    public void cooperative() {
        try {
            for (;;) {
                Thread.sleep(500);
            }
        } catch (InterruptedException e) {
            System.err.println("cooperative() interrupted");
        } finally {
            System.err.println("cooperative() finally");
        }
    }
    
    public void noncooperative() {
        try {
            for (;;) {
                Thread.yield();
            }
        } finally {
            System.err.println("noncooperative() finally");
        }
    }
    

    最后,我编写了测试(JUnit 4)来练习它们:

    @Test
    public void testStopCooperative() {
        testStop(new Runnable() {
            @Override
            public void run() {
                cooperative();
            }
        });
    }
    
    @Test
    public void testStopNoncooperative() {
        testStop(new Runnable() {
            @Override
            public void run() {
                noncooperative();
            }
        });
    }
    

    我以前从未使用过 Thread.stop(),所以我不知道它的操作。它的工作原理是从目标线程当前正在运行的任何地方抛出一个 ThreadDeath 对象。这扩展了错误。因此,虽然它并不总是干净地工作,但它通常会使简单的程序具有相当合理的程序状态。例如,调用任何 finally 块。如果你想成为一个真正的混蛋,你可以catch ThreadDeath(或错误),然后继续运行!

    如果没有别的,这真的让我希望更多的代码遵循 IProgressMonitor 方法 - 向可能需要一段时间的方法添加另一个参数,并鼓励方法的实现者偶尔轮询 Monitor 对象看看用户是否希望系统放弃。将来我会尝试遵循这种模式,尤其是可能是交互式的方法。当然,你不一定事先知道会以这种方式使用哪些方法,但我猜这就是 Profiler 的用途。

    至于“完全启动另一个 JVM”方法,这将需要更多的工作。我不知道是否有人编写了委托类加载器,或者是否包含在 JVM 中,但这种方法需要这样做。

    【讨论】:

      【解决方案5】:

      没有人直接回答,所以这是我可以用少量伪代码给你的最接近的东西:

      将方法包装在可运行/可调用的中。如果要停止该方法本身,则必须检查中断状态(例如,如果此方法是循环,则在循环内部检查 Thread.currentThread().isInterrupted ,如果是,则停止循环(不要但不要检查每次迭代,否则你只会减慢速度。 在包装方法中,使用 thread.join(timeout) 等待您想让方法运行的时间。或者,在那里的一个循环中,如果您在等待时需要做其他事情,则以较小的超时重复调用 join 。如果方法没有完成,加入后,使用上述建议快速中止/干净。

      所以代码明智,旧代码:

      void myMethod()
      {
          methodTakingAllTheTime();
      }
      

      新代码:

      void myMethod()
      {
          Thread t = new Thread(new Runnable()
              {
                  public void run()
                  {
                       methodTakingAllTheTime(); // modify the internals of this method to check for interruption
                  }
              });
          t.join(5000); // 5 seconds
          t.interrupt();
      }
      

      但同样,为了使其正常工作,您仍然必须修改 methodTakingAllTheTime 否则该线程将在您调用中断后继续运行。

      【讨论】:

      • 实际上,你不会到达 t.interrupt()。 Thread.join 将通过中断异常,而 t.interrupt 将被绕过。实际上,由于检查了 InterruptedException,上面的代码不会编译。嗯……我应该在我毁掉自己之前检查一下自己……
      • 我错了 thread.join 对 interruptedException 的使用。它只会继续。所以你的代码是正确的,以缺少的 throws 声明为模。
      【解决方案6】:

      我相信,正确的答案是创建一个 Runnable 来执行子程序,并在单独的线程中运行它。 Runnable 可能是一个 FutureTask,您可以使用超时(“get”方法)运行它。如果超时,你会得到一个 TimeoutException,我建议你在其中

      • 调用 thread.interrupt() 以尝试以半合作的方式结束它(许多库调用似乎对此很敏感,因此它可能会起作用)
      • 稍等(Thread.sleep(300))
      • 然后,如果线程仍处于活动状态 (thread.isActive()),则调用 thread.stop()。这是一种已弃用的方法,但显然是城里唯一没有运行一个单独进程的游戏。

      在我的应用程序中,我运行由我的初学者编写的不受信任、不合作的代码,我执行上述操作,确保被杀死的线程永远无法(写入)访问任何在其死亡后幸存下来的对象。这包括包含被调用方法的对象,如果发生超时,该对象将被丢弃。 (我告诉我的学生避免超时,因为他们的代理将被取消资格。)我不确定内存泄漏...

      我区分了长运行时(方法终止)和硬超时 - 硬超时更长,旨在捕捉代码根本不终止的情况,而不是速度慢。

      根据我的研究,Java 似乎没有不推荐的运行非协作代码的规定,这在某种程度上是安全模型中的一个巨大漏洞。要么我可以运行外部代码并控制它拥有的权限(SecurityManager),要么我不能运行外部代码,因为它最终可能会占用整个 CPU,而没有不推荐的方法来阻止它。

      double x = 2.0;  
      while(true) {x = x*x}; // do not terminate
      System.out.print(x); // prevent optimization
      

      【讨论】:

        【解决方案7】:

        我能想到一个不太好的方法来做到这一点。如果您可以检测到何时花费了太多时间,则可以让该方法在每个步骤中检查布尔值。如果花费太多时间,让程序将 boolean tooMuchTime 的值更改为 true(我对此无能为力)。然后使用这样的东西:

         Method(){
         //task1
        if (tooMuchTime == true) return;
         //task2
        if (tooMuchTime == true) return;
         //task3
        if (tooMuchTime == true) return;
        //task4
        if (tooMuchTime == true) return;
        //task5
        if (tooMuchTime == true) return;
        //final task
          }
        

        【讨论】:

          猜你喜欢
          • 2016-10-18
          • 2017-01-04
          • 1970-01-01
          • 2019-12-30
          • 2023-03-21
          • 1970-01-01
          • 2014-04-20
          • 2017-03-11
          • 1970-01-01
          相关资源
          最近更新 更多