如何包装一个方法,以便在超过指定超时时终止其执行?

发布于 2024-07-07 14:17:05 字数 546 浏览 7 评论 0原文

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

我正在使用Java。

举例来说:

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

我认为 TestExecutor 类应该实现 Callable 并继续朝这个方向发展。

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

建议...?

编辑

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

I have a method that I would like to call. However, I'm looking for a clean, simple way to kill it or force it to return if it is taking too long to execute.

I'm using Java.

to illustrate:

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

I figure the TestExecutor class should implement Callable and continue in that direction.

But all i want to be able to do is stop executor.execute() if it's taking too long.

Suggestions...?

EDIT

Many of the suggestions received assume that the method being executed that takes a long time contains some kind of loop and that a variable could periodically be checked.
However, this is not the case. So something that won't necessarily be clean and that will just stop the execution whereever it is is acceptable.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

心清如水 2024-07-14 14:17:05

你应该看看这些类:
FutureTask可调用执行器

这是一个示例:

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");
        }
    }
}

You should take a look at these classes :
FutureTask, Callable, Executors

Here is an example :

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");
        }
    }
}
浪漫之都 2024-07-14 14:17:05

Java 的中断机制就是针对这种场景的。 如果您希望中止的方法正在执行循环,只需让它检查线程的 每次迭代的中断状态。 如果被中断,则抛出 InterruptedException。

然后,当您想中止时,只需调用 在适当的线程上中断

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

Java's interruption mechanism is intended for this kind of scenario. If the method that you wish to abort is executing a loop, just have it check the thread's interrupted status on every iteration. If it's interrupted, throw an InterruptedException.

Then, when you want to abort, you just have to invoke interrupt on the appropriate thread.

Alternatively, you can use the approach Sun suggest as an alternative to the deprecated stop method. This doesn't involve throwing any exceptions, the method would just return normally.

回忆追雨的时光 2024-07-14 14:17:05

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

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

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

I'm assuming the use of multiple threads in the following statements.

I've done some reading in this area and most authors say that it's a bad idea to kill another thread.

If the function that you want to kill can be designed to periodically check a variable or synchronization primitive, and then terminate cleanly if that variable or synchronization primitive is set, that would be pretty clean. Then some sort of monitor thread can sleep for a number of milliseconds and then set the variable or synchronization primitive.

淡淡の花香 2024-07-14 14:17:05

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

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

有关如何实现协作方法的详细说明,请查看 Sun 关于已弃用的 Thread 方法的常见问题解答

有关现实生活中这种方法的示例,Eclipse RCP 的 Job API“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 块。 如果您想成为一个真正的混蛋,您可以捕获 ThreadDeath(或错误),并继续运行,无论如何!

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

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

Really, you can't... The only way to do it is to either use thread.stop, agree on a 'cooperative' method (e.g. occassionally check for Thread.isInterrupted or call a method which throws an InterruptedException, e.g. Thread.sleep()), or somehow invoke the method in another JVM entirely.

For certain kinds of tests, calling stop() is okay, but it will probably damage the state of your test suite, so you'll have to relaunch the JVM after each call to stop() if you want to avoid interaction effects.

For a good description of how to implement the cooperative approach, check out Sun's FAQ on the deprecated Thread methods.

For an example of this approach in real life, Eclipse RCP's Job API's 'IProgressMonitor' object allows some management service to signal sub-processes (via the 'cancel' method) that they should stop. Of course, that relies on the methods to actually check the isCancelled method regularly, which they often fail to do.

A hybrid approach might be to ask the thread nicely with interrupt, then insist a couple of seconds later with stop. Again, you shouldn't use stop in production code, but it might be fine in this case, esp. if you exit the JVM soon after.

To test this approach, I wrote a simple harness, which takes a runnable and tries to execute it. Feel free to comment/edit.

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");
}

To test it, I wrote two competing infinite loops, one cooperative, and one that never checks its thread's interrupted bit.

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");
    }
}

Finally, I wrote the tests (JUnit 4) to exercise them:

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

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

I had never used Thread.stop() before, so I was unaware of its operation. It works by throwing a ThreadDeath object from whereever the target thread is currently running. This extends Error. So, while it doesn't always work cleanly, it will usually leave simple programs with a fairly reasonable program state. For example, any finally blocks are called. If you wanted to be a real jerk, you could catch ThreadDeath (or Error), and keep running, anyway!

If nothing else, this really makes me wish more code followed the IProgressMonitor approach - adding another parameter to methods that might take a while, and encouraging the implementor of the method to occasionally poll the Monitor object to see if the user wants the system to give up. I'll try to follow this pattern in the future, especially methods that might be interactive. Of course, you don't necessarily know in advance which methods will be used this way, but that is what Profilers are for, I guess.

As for the 'start another JVM entirely' method, that will take more work. I don't know if anyone has written a delegating class loader, or if one is included in the JVM, but that would be required for this approach.

自由如风 2024-07-14 14:17:05

没有人直接回答这个问题,所以这是我可以用少量伪代码为您提供的最接近的东西:

将方法包装在可运行/可调用中。 如果您希望该方法停止,则该方法本身必须检查中断状态(例如,如果此方法是一个循环,则在循环内检查 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 ,否则该线程将在您调用中断后继续运行。

Nobody answered it directly, so here's the closest thing i can give you in a short amount of psuedo code:

wrap the method in a runnable/callable. The method itself is going to have to check for interrupted status if you want it to stop (for example, if this method is a loop, inside the loop check for Thread.currentThread().isInterrupted and if so, stop the loop (don't check on every iteration though, or you'll just slow stuff down.
in the wrapping method, use thread.join(timeout) to wait the time you want to let the method run. or, inside a loop there, call join repeatedly with a smaller timeout if you need to do other things while waiting. if the method doesn't finish, after joining, use the above recommendations for aborting fast/clean.

so code wise, old code:

void myMethod()
{
    methodTakingAllTheTime();
}

new code:

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();
}

but again, for this to work well, you'll still have to modify methodTakingAllTheTime or that thread will just continue to run after you've called interrupt.

故人如初 2024-07-14 14:17:05

我认为正确的答案是创建一个 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

The correct answer is, I believe, to create a Runnable to execute the sub-program, and run this in a separate Thread. THe Runnable may be a FutureTask, which you can run with a timeout ("get" method). If it times out, you'll get a TimeoutException, in which I suggest you

  • call thread.interrupt() to attempt to end it in a semi-cooperative manner (many library calls seem to be sensitive to this, so it will probably work)
  • wait a little (Thread.sleep(300))
  • and then, if the thread is still active (thread.isActive()), call thread.stop(). This is a deprecated method, but apparently the only game in town short of running a separate process with all that this entails.

In my application, where I run untrusted, uncooperative code written by my beginner students, I do the above, ensuring that the killed thread never has (write) access to any objects that survive its death. This includes the object that houses the called method, which is discarded if a timeout occurs. (I tell my students to avoid timeouts, because their agent will be disqualified.) I am unsure about memory leaks...

I distinguish between long runtimes (method terminates) and hard timeouts - the hard timeouts are longer and meant to catch the case when code does not terminate at all, as opposed to being slow.

From my research, Java does not seem to have a non-deprecated provision for running non-cooperative code, which, in a way, is a gaping hole in the security model. Either I can run foreign code and control the permissions it has (SecurityManager), or I cannot run foreign code, because it might end up taking up a whole CPU with no non-deprecated means to stop it.

double x = 2.0;  
while(true) {x = x*x}; // do not terminate
System.out.print(x); // prevent optimization
北城挽邺 2024-07-14 14:17:05

我能想到一个不太好的方法来做到这一点。 如果您可以检测到何时花费了太多时间,则可以让该方法在每个步骤中检查布尔值。 如果程序花费了太多时间(我无法帮助解决这个问题),则让程序将布尔值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
  }

I can think of a not so great way to do this. If you can detect when it is taking too much time, you can have the method check for a boolean in every step. Have the program change the value of the boolean tooMuchTime to true if it is taking too much time (I can't help with this). Then use something like this:

 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
  }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文