有时限的计算



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

def expensiveComputation(): Double = //... some intensive math
val result: Option[Double] = timeLimited( 45 ) { expensiveComputation() }

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

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

  • 在性能和内存方面相当便宜
  • 将在当前线程中运行有时间限制的任务

有什么建议吗?

编辑

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

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

@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)
}

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

据我所知,要么你屈服(对某个调度程序的计算调用(,要么你使用一个线程,它可以从"外部"进行操作。

如果您想在当前线程中运行任务,并且不应该涉及其他线程,则必须检查expensiveComputation内部的时间限制是否已过。例如,如果expensiveComputation是一个循环,则可以在每次迭代后检查时间。

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

我不认为有任何解决方案适用于任意的expensiveComputation代码。问题是你准备用什么来限制开支计算。

您也有不推荐使用且相当不安全的Thead.stop(Throwable)。如果您的代码除了自己创建的对象之外没有修改任何对象,那么它可能会工作。

我看到这样的模式适用于限时任务(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中重现。

对于通用解决方案(不必使用checkTimeout((代码来处理每个开销计算(,可以使用Javassist。http://www.csg.is.titech.ac.jp/~chiba/javassist/
然后可以动态插入各种checkTimeout((方法
以下是他们网站上的介绍文本:

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

面向方面编程:Javassist是一个很好的工具,可以将新方法添加到类中,并在调用方和被调用方插入before/after/around建议。

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

在当前线程中??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);
}

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

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

相关内容

  • 没有找到相关文章

最新更新