我正在迭代Iterator
,其中hasNext()
永远不会返回false。但是,在指定的时间之后(让我们说20秒),我想停止迭代。问题是next()
的{{1}}方法是阻塞的,但即便如此,在指定的时间之后,我只需要停止迭代。
以下是我的示例Iterator
和Iterable
来模拟我的问题。
Iterator
这是我要测试的代码。
public class EndlessIterable implements Iterable<String> {
static class EndlessIterator implements Iterator<String> {
public boolean hasNext() { return true; }
public String next() {
return "" + System.currentTimeMillis(); //in reality, this code does some long running task, so it's blocking
}
}
public Iterator<String> iterator() { return new EndlessIterator(); }
}
我想将代码/逻辑放入EndlessIterable iterable = new EndlessIterable();
for(String s : iterable) { System.out.println(s); }
类来创建Iterable
,因此在指定的时间结束后,将抛出异常以停止迭代。
Timer
然后我尝试按如下方式测试此代码。
public class EndlessIterable implements Iterable<String> {
static class EndlessIterator implements Iterator<String> {
public boolean hasNext() { return true; }
public String next() {
try { Thread.sleep(2000); } catch(Exception) { } //just sleep for a while
return "" + System.currentTimeMillis(); //in reality, this code does some long running task, so it's blocking
}
}
static class ThrowableTimerTask extends TimerTask {
private Timer timer;
public ThrowableTimerTask(Timer timer) { this.timer = timer; }
public void run() {
this.timer.cancel();
throw new RuntimeException("out of time!");
}
}
private Timer timer;
private long maxTime = 20000; //20 seconds
public EndlessIterable(long maxTime) {
this.maxTime = maxTime;
this.timer = new Timer(true);
}
public Iterator<String> iterator() {
this.timer.schedule(new ThrowableTimerTask(this.timer), maxTime, maxTime);
return new EndlessIterator();
}
}
我注意到的是,EndlessIterable iterable = new EndlessIterable(5000);
try {
for(String s : iterable) { System.out.println(s); }
} catch(Exception) {
System.out.println("exception detected: " + e.getMessage());
}
System.out.println("done");
在时间结束后被抛出,
我已经描述过解决这个问题的任何策略,方法或设计模式?
请注意
RuntimeException
Iterator
和实际迭代答案 0 :(得分:1)
您的工作使用了错误的工具。如果要使操作超时,则必须将检查添加到操作中。建议将普通迭代器逻辑与超时检查分开,这似乎符合您的注释,即您无法更改Iterator
实现。为此,请使用装饰器 / 委派模式:
// an iterator wrapping another one adding the timeout functionality
class TimeOutIterator<T> implements Iterator<T> {
final Iterator<T> source;
final long deadline;
public TimeOutIterator(Iterator<T> dataSource, long timeout, TimeUnit unit) {
source=dataSource;
deadline=System.nanoTime()+unit.toNanos(timeout);
}
private void check() {
if(System.nanoTime()-deadline >= 0)
throw new RuntimeException("timeout reached");
}
public boolean hasNext() {
check();
return source.hasNext();
}
public T next() {
check();
return source.next();
}
public void remove() {
check();
source.remove();
}
}
所以你可以将你的iterable实现为:
public class EndlessIterable implements Iterable<String> {
static class EndlessIterator implements Iterator<String> {
public boolean hasNext() { return true; }
public String next() {
// dummy code illustrating the long running task
try { Thread.sleep(2000); } catch(Exception e) { }
return "" + System.currentTimeMillis();
}
public void remove() { throw new UnsupportedOperationException(); }
}
private long maxTime;
private TimeUnit unit;
public EndlessIterable(long maxTime, TimeUnit timeUnit) {
this.maxTime = maxTime;
this.unit = timeUnit;
}
public Iterator<String> iterator() {
return new TimeOutIterator<>(new EndlessIterator(), maxTime, unit);
}
}
然后测试代码如下:
// should timeout after five seconds
EndlessIterable iterable = new EndlessIterable(5, TimeUnit.SECONDS);
try {
for(String s : iterable) { System.out.println(s); }
} catch(Exception e) {
System.out.println("exception detected: " + e);
}
System.out.println("done");
答案 1 :(得分:1)
没有一般方法可以停止所有阻止操作。有些调用是可中断的,也就是说,当另一个线程在工作线程上调用interrupt()
时,它们会因错误或部分结果而中止其操作。其他时候,有些黑客会导致操作终止;例如,如果线程被阻塞读取套接字,则另一个线程可以关闭套接字。
但是,在这种情况下,您可以执行生成另一个线程中的下一个元素所需的阻塞操作。将实现迭代器以使用BlockingQueue
中的这些元素。这样,迭代线程将在超时到期时立即返回,而不是等待最后一个元素的无限量额外时间。工作线程可能会持续一段时间,但它可以在生成每个元素之前检查一个标志,以确定是否继续。
您已经接受了答案,但如果您对此方法感兴趣,请告诉我,我可以草拟一些代码。
答案 2 :(得分:0)
你可以在Thread中开始这个任务,在另一个线程中你将启动类似
的东西Thread.sleep(20)
然后取消第一个帖子
答案 3 :(得分:0)
让一个线程在你的持续时间内休眠,然后在正在进行迭代的线程上调用Thread.interrupt()
,然后每次通过循环,你可以检查线程是否isInterrupted()
并退出循环,如果它是真的。
您还可以使用Timer
代替内联调用Thread.sleep()
;哪种方法更适合您将取决于您的调用线程正在做什么等等。