我有很多Java字节码的例子,所有这些我都想从Clojure执行。每个字节码序列可能包含一个无限循环,在这种情况下,我希望在几秒钟后停止运行它。我一直将未来视为实现这一目标的一种手段。在寻找了几个实现之后,我尝试了这两种代码:
(deref (future (loop[a 1] (recur a)) :done!) 1000 :impatient!)
以及https://gist.github.com/3124000
在这两种情况下,循环似乎都适当地超时了(在后一种情况中,报告称未来已经完成并取消),但我看到我的CPU使用率上升到99%左右,并保持不变。我还看到,每次运行这些代码时,我的Java进程都会获得一个额外的线程。
在我看来,未来似乎被取消了,但代码仍在运行。在我的程序中,我需要运行一些非常紧密的无限循环(例如,相当于"20 PRINT GOTO 10"的Java字节码),并且超时,我无法修改正在运行的代码。
知道我为什么会看到这种行为吗;我能做些什么来防止它;或者让我实现运行和计时这些代码的目标的其他方法?
Java支持的线程取消机制是中断。.stop()
方法被弃用是有原因的——请参阅文档、Effective Java、Java Concurrency in Practice等
事实上,FutureTask
(支持future-cancel
)的实现是基于中断的。
到目前为止,每个Clojure future都有一个无边界线程池作为支持(就像send-off
操作一样)。因此,您可以利用线程中断原语:
(def worker
(let [p (promise)]
{:future (future
(let [t (Thread/currentThread)]
(deliver p t)
(while (not (Thread/interrupted))
(println 42)
(Thread/sleep 400))))
:thread @p}))
现在可以成功地执行(.interrupt (:thread worker))
或(future-cancel (:future worker))
。
尽管这将线程取消机制与Futures的线程取消机制相结合,但可用的Executor实现足够智能,可以清除先前任务可能已设置的中断状态,因此一个任务的中断永远不会影响其他任务,即使它们在同一线程上运行。
关键是,先发制人地杀死线程通常是个坏主意——如果你想取消任务,你必须以某种或其他方式明确地取消它们。OTOH,在您的特定情况下,您正在执行不透明的字节码,因此除了求助于.stop()
之外别无选择。
我发现的真正杀死线程内执行的代码的唯一方法是使用不推荐使用的.stop
方法。在很多情况下,它被弃用的原因其实并不重要。
我就是因为那样做才在监狱里受到惩罚的。可以随意取出函数,也可以直接插入库中。
根据Java API文档,cancel
不能立即停止任务。
尝试取消执行此任务。如果任务已完成、已取消或由于其他原因无法取消,则此尝试将失败。如果成功,并且在调用cancel时此任务尚未启动,则不应运行此任务。如果任务已经启动,那么mayInterruptIfRunning参数确定是否应该中断执行此任务的线程以尝试停止任务。