计算有时间限制
我正在尝试编写一个允许我在给定时间窗口内运行计算的构造。 就像是:
def expensiveComputation(): Double = //... some intensive math val result: Option[Double] = timeLimited( 45 ) { expensiveComputation() }
这里timeLimited
将运行expensiveComputation
,超时为45分钟。 如果达到超时,则返回None
,否则将结果包装为Some
。
我正在寻找一个解决方案:
- 在性能和记忆方面相当便宜;
-
将在当前线程中运行限时任务。
有什么建议吗?
编辑
我理解我的原始问题没有解决方案。 假设我可以为计算创建一个线程(但我不想使用线程池/执行器/调度程序)。 什么是最快,最安全,最干净的方法?
运行给定的代码块或在超时时抛出exception:
@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期货?
据我所知, 要么屈服 (对某些调度程序的计算调用), 要么使用一个从“外部”操纵的线程 。
如果要在当前线程中运行任务,并且如果不涉及其他线程,则必须检查costComputation中的时间限制是否结束。 例如,如果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 Programming Assistant)使Java字节码操作变得简单。 它是一个用于在Java中编辑字节码的类库; 它使Java程序能够在运行时定义新类,并在JVM加载时修改类文件。 与其他类似的字节码编辑器不同,Javassist提供两个级别的API:源级别和字节码级别。 如果用户使用源级API,他们可以编辑类文件而不需要了解Java字节码的规范。 整个API仅使用Java语言的词汇表进行设计。 您甚至可以以源文本的forms指定插入的字节码; Javassist即时编译它。 另一方面,字节码级API允许用户直接编辑类文件作为其他编辑器。
面向方面编程:Javassist可以成为一个很好的工具,可以将新方法添加到类中,也可以在调用方和被调用方之间插入/之前/之后的建议。
反思:Javassist的一个应用是运行时reflection; Javassist使Java程序能够使用控制基础对象上的方法调用的元对象。 不需要专门的编译器或虚拟机。
在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); }
如果你非常认真地需要这个,你可以创建一个编译器插件,在循环和条件中插入检查块。 然后,这些检查块可以检查Thread.isInterrupted()并抛出exception以进行转义。
您可以使用注释(即@interruptible)来标记要增强的方法。