【问题标题】:JAVA: How to stop the execution of a function after a specific time?JAVA:如何在特定时间后停止执行函数?
【发布时间】:2023-05-03 08:54:01
【问题描述】:

我想实现迭代深化(增量树构建)。这是我要询问的代码的一部分:

        ExecutorService executorService = Executors.newSingleThreadExecutor();

        Set<Callable<Integer>> callables = new HashSet<Callable<Integer>>();

        callables.add(new Callable<Integer>() {
            public Integer call() throws Exception {
                iterativeDeepening(depthLimit, board);
                return -1;
            }
        });
        callables.add(new Callable<Integer>() {
            public Integer call() throws Exception {
                Thread.sleep(500);
                return 1;
            }
        });
        try{
            executorService.invokeAny(callables, 1000, TimeUnit.MILLISECONDS);
        }catch(TimeoutException | InterruptedException ex){
            executorService.shutdown();
        }

        executorService.shutdown();

根据我所读到的有关具有时间限制的 invokeAny() 的内容,它应该在达到最后期限后立即结束执行其 Callable 对象。当我长时间睡眠而不是我的函数 iterativeDeepening(depthLimit, board) 时,它会起作用。如何使它与我的功能一起使用? 下面我将代码粘贴到这个函数中:

    public void iterativeDeepening(byte depthLimit, byte[] board){

    for(byte depth=1;depth<depthLimit;depth++){
        GameTree gameTree= new GameTree();
        byte[] tempBoard = new byte[14];
        for(byte i=0;i<14;i++){
            tempBoard[i] = board[i];
        }
        Node <byte[]> root= new Node<byte[]>(tempBoard, player);
        try {
            gameTree.buildGameTree(depth, root);
        } catch (OutOfMemoryError E) {
            gameTree.eraseGameTree(depth,root);
            System.gc();
        }

        MiniMax minimax = new MiniMax(player);
        move= minimax.selectMove(depth, root);

    }
}

如果您知道更好的方法或知道如何成功停止执行我的功能,请告诉我。我还尝试了本主题中提到的可运行接口: How to stop execution after a certain time in Java? 但它的工作原理是一样的。

【问题讨论】:

  • 如果你希望它是自包含的,你可以在进入循环之前存储一个 System.currentTimeMillis() 的变量。然后在循环的每次迭代中检查if (System.currentTimeMillis() - startTime &gt;= maxRunTime)
  • 这不是一个解决方案,因为在循环中有一个函数 gameTree.buildGameTree(depth, root);这本身有时需要比最后期限更长的时间,这是至关重要的。
  • 您可以将开始时间作为参数传入。 @AndreyChaschev 可能是正确的,您最好的选择就是检查中断。中断一个线程并不一定会在不自己检查中断的情况下提前结束它。
  • 我已经尝试了以下解决方案,起初我认为这是合法的,但它对我不起作用 - 也许这是我的错误(我会稍后粘贴代码,因为截止日期即将到来)。将时间作为参数传递是非常简单和好主意。我现在就试试。谢谢!
  • 如果检查中断不起作用,您可以尝试的另一件事是休眠一些微不足道的时间,例如 1ms 并捕获 InterruptedException。我不知道为什么,但是在没有看到标志的跑掉线程的情况下,我很幸运。

标签: java executorservice execution-time callable iterative-deepening


【解决方案1】:

达到超时后,ExecutorService 将尝试通过调用Thread.interrupt() 来中断所有当前正在运行的任务。这将使每个线程进入中断状态。 sleep() 在设置此状态时退出。

所以添加这个检查:

if(Thread.currentThread().interrupted()) {
    return;
}

在你的函数内部应该可以完成这项工作。

线程终止提示:

try{
    executorService.invokeAny(callables, 1000, TimeUnit.MILLISECONDS);
} catch(TimeoutException | InterruptedException ex){
    //... ignore
} finally {
    executorService.shutdown();
    executorService.awaitTermination(); <-- add this line if you want to wait for the computation to end
}

更新

这不是解决方案,因为在循环中有一个函数 gameTree.buildGameTree(depth, root);这本身有时需要比最后期限更长的时间,这是至关重要的。

据我所知,没有办法从外部中断这种功能。这个函数应该不时检查它的状态。如果是循环,请考虑检查部分或全部迭代的状态。

【讨论】:

  • 不...不幸的是,它不是一个循环,而是一个递归函数,它的行为非常奇怪。在(buildGameTree(...)的开头)我添加了 startTime 作为参数,并且我在开始时检查时间是否结束。如果是这样,我将返回一个被捕获并返回结果的异常。奇怪的是,时间限制经常被超过甚至2-3次。我尝试使用 nanoTime() 和 currentTimeMillis() 得到相同的结果。
  • 您可能需要在线程的顶层显式捕获异常。
  • 为什么不直接呢?由于我开始将时间作为论据,我将这两个函数展平。捕捉看起来像这样:尝试 {gameTree.buildGameTree(depth, root, startTime, timeLimit); } catch (OutOfMemoryError | Exception e) {return move;}
  • 因为如果至少有一个成功完成的任务,invokeAny 不会从您的线程传播异常。在这种情况下,您不会注意到发生了任何错误。
最近更新 更多