计算有时间限制

时间:2011-10-04 16:47:37

标签: java scala timeout

我正在尝试编写一个允许我在给定时间窗口内运行计算的构造。类似的东西:

def expensiveComputation(): Double = //... some intensive math

val result: Option[Double] = timeLimited( 45 ) { expensiveComputation() }

此处timeLimited将运行expensiveComputation,超时时间为45分钟。如果达到超时,则返回None,否则将结果包装到Some

我正在寻找一个解决方案:

  • 在性能和记忆方面相当便宜;
  • 将在当前主题中运行有时间限制的任务。

有什么建议吗?

修改

我理解我的原始问题没有解决方法。假设我可以为计算创建一个线程(但我更喜欢不使用线程池/执行器/调度程序)。什么是最快,最安全,最干净的方法?

9 个答案:

答案 0 :(得分:8)

运行给定的代码块或在超时时抛出异常:

@throws(classOf[java.util.concurrent.TimeoutException])
def timedRun[F](timeout: Long)(f: => F): F = {

  import java.util.concurrent.{Callable, FutureTask, TimeUnit}

  val task = new FutureTask(new Callable[F]() {
    def call() = f
  })

  new Thread(task).start() 

  task.get(timeout, TimeUnit.MILLISECONDS)
}

答案 1 :(得分:3)

只有一个想法:我对akka futures不太熟悉。但也许有可能将未来的执行线程粘贴到当前线程并使用带有超时的akka​​期货?

答案 2 :(得分:2)

据我所知,要么屈服(对某些调度程序的计算调用),要么使用线程,这将从“外部”进行操作。

答案 3 :(得分:1)

如果要在当前线程中运行任务,并且如果不涉及其他线程,则必须检查expensiveComputation内的时间限制是否结束。例如,如果expensiveComputation是循环,则可以在每次迭代后检查时间。

答案 4 :(得分:1)

如果您可以expensiveComputation的代码经常检查Thread.interrupted(),那就很容易了。但我想你不是。

我认为没有任何解决方案适用于任意expensiveComputation代码。 问题是你准备好了什么作为昂贵的计算的约束。

您也已弃用且非常不安全Thead.stop(Throwable)。如果您的代码不修改任何对象,而是修改它自己创建的对象,它可能会起作用。

答案 5 :(得分:1)

我看到这样的模式适用于有时间限制的任务(Java代码):

try {
    setTimeout(45*60*1000); // 45 min in ms
    while (not done) {
       checkTimeout();
       // do some stuff
       // if the stuff can take long, again:
       checkTimeout();
       // do some more stuff
    }
    return Some(result);
}
catch (TimeoutException ex) {
    return None;
}

checkTimeout()功能调用便宜;你将它添加到代码中,以便合理地调用它,但不是经常。它只是根据setTimeout()设置的定时器值加上超时值来检查当前时间。如果当前时间超过该值,则checkTimeout()会引发TimeoutException

我希望这个逻辑也可以在Scala中重现。

答案 6 :(得分:1)

对于通用解决方案(不必使用checkTimeout()代码丢弃每个昂贵的计算)也许使用Javassist。 http://www.csg.is.titech.ac.jp/~chiba/javassist/
然后,您可以动态插入各种checkTimeout()方法 以下是其网站上的介绍文字:

Javassist(Java Programming Assistant)使Java字节码操作变得简单。它是一个用Java编辑字节码的类库;它使Java程序能够在运行时定义新类,并在JVM加载时修改类文件。与其他类似的字节码编辑器不同,Javassist提供两个级别的API:源级别和字节码级别。如果用户使用源级API,他们可以在不知道Java字节码规范的情况下编辑类文件。整个API仅使用Java语言的词汇表进行设计。您甚至可以以源文本的形式指定插入的字节码; Javassist即时编译它。另一方面,字节码级API允许用户直接编辑类文件作为其他编辑器。

面向方面编程:Javassist可以成为一个很好的工具,可以将新方法添加到类中,也可以在调用方和被调用方之前插入/之后/周围的建议。

反思:Javassist的一个应用程序是运行时反射; Javassist使Java程序能够使用控制基础对象上的方法调用的元对象。不需要专门的编译器或虚拟机。

答案 7 :(得分:0)

在currentThread? Phhhew ... 在计算中的每个步骤后检查 好吧,如果您的“昂贵的计算”可以分解为多个步骤或具有迭代逻辑,您可以捕获启动时的时间,然后定期检查您的步骤。这绝不是一般的解决方案,但可行。

对于更通用的解决方案,您可以使用方面或注释处理,通过这些检查自动填充您的代码。如果“检查”告诉您时间到了,则返回无。

使用注释和注释处理器快速思考java中的解决方案......

public abstract Answer{}
public class Some extends Answer {public Answer(double answer){answer=answer}Double answer = null;}
public class None extends Answer {}


//This is the method before annotation processing
@TimeLimit(45)
public Answer CalculateQuestionToAnswerOf42() {
 double fairydust = Math.Pi * 1.618;
 double moonshadowdrops = (222.21) ^5;
 double thedevil == 222*3;
 return new Answer(fairydust + moonshadowdrops + thedevil);
}

//After annotation processing
public Answer calculateQuestionToAnswerOf42() {
 Date start = new Date() // added via annotation processing;
 double fairydust = Math.Pi * 1.618; 
 if(checkTimeout(start, 45)) return None; // added via annotation processing;
 double moonshadowdrops = (222.21) ^5;
 if(checkTimeout(start, 45)) return None; // added via annotation processing;
 double thedevil == 222*3;
 if(checkTimeout(start, 45)) return None; // added via annotation processing;
 return new Answer(fairydust + moonshadowdrops + thedevil);
}

答案 8 :(得分:0)

如果你非常认真地需要这个,你可以创建一个编译器插件,在循环和条件中插入检查块。然后,这些检查块可以检查Thread.isInterrupted()并抛出异常以进行转义。

您可以使用注释,即@interruptible,来标记要增强的方法。