繁体   English   中英

在特定时间之后突破迭代的最佳方法是什么?

[英]What is the best way to break out of iteration after a specific time?

我正在迭代迭代Iterator ,其中hasNext()永远不会返回false。 但是,在指定时间(比方说20秒)之后,我想停止迭代。 问题是Iteratornext()方法是阻塞的,但即便如此,在指定的时间之后,我只需要迭代来停止。

这是我的示例IterableIterator来模拟我的问题。

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 ,但是,

  • for循环继续,
  • 永远不会到达捕获块,并且
  • 我永远不会到达代码的末尾(完成打印)。

我已经描述过解决这个问题的任何策略,方法或设计模式?

请注意

  • 在我的实际代码中,我无法控制Iterator
  • 我只能控制Iterable和实际迭代

你正在使用错误的工具来完成工作。 如果要使操作超时,则必须将检查添加到操作中。 建议将普通迭代器逻辑与超时检查分开,这似乎符合您的说法,即您无法更改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");

没有通用的方法来阻止所有阻塞操作。 有些调用是可中断的,也就是说,当另一个线程在工作线程上调用interrupt()时,它们会中止错误或部分结果。 其他时候,有些黑客会导致操作终止; 例如,如果线程被阻塞读取套接字,则另一个线程可以关闭套接字。

但是,在这种情况下,您可以执行生成另一个线程中的下一个元素所需的阻塞操作。 迭代器将被实现为从BlockingQueue使用这些元素。 这样,迭代线程将在超时到期时立即返回,而不是等待最后一个元素的无限量额外时间。 工作线程可能会持续一段时间,但它可以在生成每个元素之前检查标志以确定是否继续。

您已经接受了答案,但如果您对此方法感兴趣,请告诉我,我可以草拟一些代码。

你可以在Thread中开始这个任务,在另一个Thread中你会开始这样的事情

Thread.sleep(20)

然后取消第一个帖子

让一个线程在你的持续时间内休眠,然后在正在进行迭代的线程上调用Thread.interrupt() ,然后每次通过循环,你可以检查线程是否为isInterrupted()并退出循环,如果它是真的。

您也可以使用Timer而不是内联调用Thread.sleep() ; 哪种方法更适合您将取决于您的调用线程正在做什么等。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM