gpt4 book ai didi

java - 如何正确阻塞线程直到超时开始?

转载 作者:行者123 更新时间:2023-12-01 12:47:11 46 4
gpt4 key购买 nike

我想并行运行多个任务,直到经过一定时间。让我们假设这些线程占用大量 CPU 资源并且/或可能无限期地阻塞。超时后,线程应立即中断,主线程应继续执行,无论未完成或仍在运行的任务。

我见过很多问这个问题的问题,答案总是相似的,通常是“为任务创建线程池,启动它,超时加入它”

问题出在“开始”和“连接”部分之间。一旦允许池运行,它可能会抢占 CPU,并且在我将其恢复之前,超时甚至不会开始。

我尝试过Executor.invokeAll,发现并不能完全满足要求。示例:

    long dt = System.nanoTime ();
ExecutorService pool = Executors.newFixedThreadPool (4);
List <Callable <String>> list = new ArrayList <> ();
for (int i = 0; i < 10; i++) {
list.add (new Callable <String> () {
@Override
public String call () throws Exception {
while (true) {
}
}
});
}
System.out.println ("Start at " + (System.nanoTime () - dt) / 1000000 + "ms");
try {
pool.invokeAll (list, 3000, TimeUnit.MILLISECONDS);
}
catch (InterruptedException e) {
}
System.out.println ("End at " + (System.nanoTime () - dt) / 1000000 + "ms");

Start at 1ms
End at 3028ms

这(27 毫秒延迟)可能看起来并不算太糟糕,但是无限循环很容易打破 - 实际程序的体验要容易十倍。我的期望是,即使在重负载下,超时请求也能以非常高的精度得到满足(我正在考虑硬件中断,它应该总是工作)。

这是我的特定程序中的一个主要问题,因为它需要相当准确地注意某些超时(例如,大约 100 毫秒,如果可能的话更好)。然而,启动池通常需要长达 400 毫秒的时间,直到我重新获得控制权,从而超过了最后期限。

我有点困惑为什么这个问题几乎从未被提及。我见过的大多数答案肯定都受到了这个问题的影响。我想这通常是可以接受的,但就我而言却不能。

有没有一种干净且经过测试的方法来解决这个问题?

编辑添加:

我的程序涉及GC,尽管规模不大。为了测试目的,我重写了上面的例子,发现给出的结果非常不一致,但平均起来明显比以前差。

    long dt = System.nanoTime ();
ExecutorService pool = Executors.newFixedThreadPool (40);
List <Callable <String>> list = new ArrayList <> ();
for (int i = 0; i < 10; i++) {
list.add (new Callable <String> () {
@Override
public String call () throws Exception {
String s = "";
while (true) {
s += Long.toString (System.nanoTime ());
if (s.length () > 1000000) {
s = "";
}
}
}
});
}
System.out.println ("Start at " + (System.nanoTime () - dt) / 1000000 + "ms");
try {
pool.invokeAll (list, 1000, TimeUnit.MILLISECONDS);
}
catch (InterruptedException e) {
}
System.out.println ("End at " + (System.nanoTime () - dt) / 1000000 + "ms");

Start at 1ms
End at 1189ms

最佳答案

invokeAll 应该可以正常工作。然而,编写任务以正确响应中断至关重要。当捕获InterruptedException时,他们应该立即退出。如果您的代码正在捕获 IOException,则每个此类 catch block 前面应带有以下内容:

} catch (InterruptedIOException e) {
logger.log(Level.FINE, "Interrupted; exiting", e);
return;
}

如果您使用Channels ,您将需要处理 ClosedByInterruptException同样的方式。

如果执行的耗时操作没有捕获上述异常,则需要检查Thread.interrupted()定期。显然,更频繁地检查会更好,尽管会有 yield 递减的情况。 (这意味着,在任务中的每个语句之后检查它可能没有用。)

if (Thread.interrupted()) {
logger.fine("Interrupted; exiting");
return;
}

在您的示例代码中,您的 Callable 根本不检查中断状态,所以我的猜测是它永远退出。中断实际上并不停止线程;而是停止线程。它只是向线程发出信号,表明它应该按照自己的方式自行终止。

关于java - 如何正确阻塞线程直到超时开始?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24586932/

46 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com