18

我有很多 Java 字节码示例,我想从 Clojure 执行所有这些示例。每个字节码序列都可能包含一个无限循环,在这种情况下,我想在几秒钟后停止运行它。我一直将期货视为这样做的一种手段。在寻找了几个实现之后,我尝试了这两个代码:

(deref (future (loop[a 1] (recur a)) :done!) 1000 :impatient!)

...以及https://gist.github.com/3124000上的代码

在这两种情况下,循环似乎都适当地超时(在后一种情况下,据报道未来已经完成并取消),但我看到我的 CPU 使用率上升到 99% 左右并保持在那里。我还看到每次运行此代码时,我的 Java 进程都会获得一个额外的线程。

在我看来,未来正在被取消,但代码仍在运行。在我的程序中,我需要运行并超时,一些非常紧凑的无限循环(例如,Java 字节码相当于“20 PRINT GOTO 10”),并且我没有修改我正在运行的代码的选项。

为什么我看到这种行为的任何想法;我能做些什么来防止它;或替代方法来实现我运行和超时此类代码的目标?

4

3 回答 3

8

Java 支持的线程取消机制是中断。该.stop()方法因某种原因而被弃用 - 请参阅文档、Effective Java、Java Concurrency in Practice 等。

事实上,FutureTask(which backs future-cancel) 的实现是基于中断的。

到今天为止,每个 Clojure 未来都由一个无界线程池支持(就像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().

于 2013-01-26T19:46:56.870 回答
4

我发现实际杀死线程内执行的代码的唯一方法是使用不推荐使用的.stop方法。在很多情况下,它被弃用的原因并不重要。

我在clojail中有一个功能可以做到这一点。随意抢夺功能或只是拉入库。

于 2012-07-17T10:37:49.370 回答
1

根据Java API doc,cancel不能立即停止任务。

尝试取消此任务的执行。如果任务已完成、已被取消或由于某些其他原因无法取消,则此尝试将失败。如果成功,并且在调用取消时此任务尚未启动,则此任务不应该运行。如果任务已经开始,则 mayInterruptIfRunning 参数确定是否应该中断执行该任务的线程以尝试停止该任务。

于 2012-07-17T11:19:31.283 回答