【问题标题】:Java - 'continue' loop iteration after certain timeout periodJava - 在特定超时时间后“继续”循环迭代
【发布时间】:2010-12-24 12:29:57
【问题描述】:

有没有办法在某个超时时间后退出('继续;')循环迭代?

我有一个循环,它将运行从网络收集数据,然后使用这些数据进行计算。

数据在大约 1 到 2 秒后过时,所以如果循环迭代时间超过 1 秒,那么我希望它“继续”到下一次迭代。

有时收集数据可能需要一些时间,但有时计算可能需要超过 1 秒的时间,因此 HTTP 超时无法满足我的需要。 此外,在进行计算时,我正在使用的线程被阻塞,因此我无法检查 System.currentTimeMillis();

有没有办法使用另一个线程来检查时间并强制原来的for循环继续。

【问题讨论】:

    标签: java android for-loop


    【解决方案1】:

    使用AsyncTask 进行阻塞计算,并让Handler 属于您的主线程。

    在您的 onPreExecute() 中,您可以在 Handler.postDelayed() 中调用 AsyncTask.cancel(true)Runnable。在您的onPostExecute() 中,您可以取消上述Runnable,因为如果计算及时完成,则不需要它。任务完成。

    【讨论】:

      【解决方案2】:

      我基于以下假设来回答这个问题:无法更改计算代码以检查 boolean stopSystem.currentTimeMillis() 等标志。如果这是真的,那么这是一个可能的解决方案。

      您需要做的是在每次预期新结果时生成一个新计算。我包含的这个程序确实存在一些问题,例如从不确保计算完成导致无限数量的线程。同样,这是基于您无法过早停止计算的假设。如果您确实有该选项,您可以在计算循环中设置一个标志以提前退出该方法。

      我不知道为什么我不能让代码样式正常工作,我是这个网站的新手,任何帮助将不胜感激

      您将维护一堆已处理的结果。如果您始终获得最佳结果,那么它将是您在该时间点可能处理的最新结果。我在这里创建一个堆栈而不是仅仅覆盖之前的结果的原因是你需要对之前的计算做一些事情。

      在我的例子中performCalculation的主体只对模拟你提到的环境很重要。

      您可以创建一个新线程,或使用现有线程不断处理抛出到results 的结果。

      import java.util.Random;
      import java.util.Stack;
      import java.util.Timer;
      import java.util.TimerTask;
      import java.util.concurrent.atomic.AtomicInteger;
      
      public class Main
      {
          private static int CALCULATION_THRESHOLD = 2000;
      
          private static Stack<Object> results = new Stack<Object>();
      
          private static Object resultTrigger = new Object();
      
          public static void main(String[] args)
          {
              Timer calculationTimer = new Timer(true);
              calculationTimer.schedule(new TimerTask() {
                  @Override
                  public void run()
                  {
                      Thread calculationThread = new Thread() {
                          public void run() {
                              Object result = performCalculation();
                              results.push(result);
                              synchronized(resultTrigger) {
                                  resultTrigger.notifyAll();
                              }       
                          }
                      };
                      calculationThread.start();
                  }
              }, CALCULATION_THRESHOLD, CALCULATION_THRESHOLD);
      
              synchronized(resultTrigger) {
                  if (results.isEmpty()) {
                      // This is bad as it will never end if you don't
                      // get a result, add a timeout here. 
                      try { resultTrigger.wait(); }
                      catch (InterruptedException ex) {}
                  }
              }
      
              // Get the next result
              Object result = results.pop();
      
              System.out.println ("Latest result is : " + result);
      
              // Do something with the remaining results or throw 
              // them away
              results.clear();
          }
      
          private static AtomicInteger counter = new AtomicInteger();
      
          // This is the method we are assuming can't be
          // changed to check for a stop flag.
          public static Object performCalculation()
          {
              int calcID = counter.addAndGet(1);
              System.out.println ("Calculation " + calcID + " is running.");
              Random randomGenerator = new Random();
              int sleep = randomGenerator.nextInt(10000);
              // Ensure we sleep for at least 2 seconds
              try { Thread.sleep(sleep + 2000);   }
              catch (InterruptedException ex) {}
              return String.valueOf(counter.get());
          }
      }
      

      示例输出:

      运行 1

      计算 1 正在运行。 计算 2 正在运行。 计算 3 正在运行。 最新结果是:3

      运行 2

      计算 1 正在运行。 计算 2 正在运行。 计算 3 正在运行。 计算 4 正在运行。 计算 5 正在运行。 最新结果是:5

      运行 3

      计算 1 正在运行。 计算 2 正在运行。 最新结果是:2

      【讨论】:

      • 目前我无法评论其他任何人的答案,AsychTask 会很棒,但它依赖于能够在计算线程中检查 isCancelled。我正在阅读这个问题,假设这不能按照此语句完成“此外,在进行计算时,我正在使用的线程被阻塞,因此我无法检查 System.currentTimeMillis();”如果用户不检查标志 isCancelled(),AsyncTask 上的 cancel(true) 不会自动取消线程。
      • +1 以获得一个不错且冗长的第一个答案。我希望您能尽快获得积分,以便您可以发表评论、赞成、反对等:)
      • Finnell:说建议使用 AsyncTask 的答案更好:)
      • @SpoonBender AysncTask 如果他能够检查 isCancelled 标志会更好。如果计算在 AsyncTask blocks 内运行 15 秒,则不会在 AsyncTask.cancel(true) 时取消。如果出于某种原因(我怀疑) AsyncTask.cancel 尝试 abort() 线程,那太可怕了。如果它试图中断线程,它仍然不能保证停止线程。一个示例将在 AsyncTask 执行中运行 while(true) { }。没有办法阻止它不涉及做一些非常糟糕的事情,比如中止一个线程。
      • 谢谢安德鲁,你所有的假设都是正确的。 “计算”是在外部 Jar 中完成的,所以我无法检查在某些情况下导致计算缓慢的原因,更不用说中途停止了。
      【解决方案3】:

      似乎最简单的解决方案是将 System.currentTimeMillis() 添加到计算本身,如果它检测到它运行时间过长而无法退出而没有结果。当线程被解除阻塞时,您将不得不检查是否有结果,如果没有则“继续”。 当然,您可以使用另一个线程,但这将是一个矫枉过正。

      【讨论】:

        【解决方案4】:

        我建议使用提交给ExecutorServiceCallable 任务并在超时的情况下运行它。代码应该很直观:

        import java.util.concurrent.*;
        
        class InterruptibleProcessing{
            public static void main(String[] args){
                ExecutorService es = Executors.newSingleThreadExecutor();
        
                //the loop
                for(int i=0; i<iterations; i++){
                    //run your data gathering process in a separate thread
                    Future<Result> futureResult = es.submit(new Callable<Result>(){
                        public Result call(){
                            //do you work here and return the result
                            return gatherData();
                        }
                    });
        
                    try{
                        //wait for result with timeout
                        Result result = futureResult.get(1, TimeUnit.SECONDS);
                        //if we are here then we have the result in less than 1 second
                        // do something and exit the loop
                        break;
                    }catch(TimeoutException timeout){
                        //Didn't finish in time, cancel the task, and proceed to
                        //next iteration. This will send an interrupt signal to 
                        //your task thread.
                        futureResult.cancel(true);
                    }
                }
            }
        }
        

        为了使其完美运行,您的数据收集任务将需要检查线程中断。这主要包括:

        1) 检查可能长时间使用 CPU 的循环内的线程中断(这不包括 I/O)。如果您有大型循环进行繁重的处理,请确保您以相对较短的时间间隔运行此代码

        if(Thread.isInterrupted()){
            throw new InterruptedException();
            //or maybe some other code to stop processing
        }
        

        2) 进行 I/O 的地方,例如从套接字或文件读取/写入,通常会检查中断并抛出某种异常。异常的种类可能是InterruptedIOExceptionClosedByInterruptException等等。抛出的异常类型通常在对应的阻塞方法的 API 中指定。阻塞 Java 锁的方法(如 Queue.take() 等)将抛出 InterruptedException

        【讨论】:

          猜你喜欢
          • 2017-08-12
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2023-01-20
          • 2017-01-30
          • 1970-01-01
          相关资源
          最近更新 更多