2012-07-17 94 views
16

我有很多Java字節碼的例子,我想從Clojure執行所有這些例子。每個字節碼序列可能包含一個無限循環,在這種情況下,我想在幾秒鐘後停止運行它。我一直在尋找期貨作爲這樣做的一種手段。已經獵殺周圍的一對夫婦實現的我都試過這個代碼:爲什麼取消的Clojure期貨繼續使用CPU?

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

...而且在https://gist.github.com/3124000

在這兩種情況下的代碼,循環似乎是適當的超時(和在後一種情況下,未來據報道已經完成並取消),但我看到我的CPU使用率上升到99%左右,並保持在那裏。我還看到,每次運行此代碼時,我的Java進程都會獲得額外的線程。

它看起來像我未來被取消,但代碼仍在運行。在我的程序中,我將需要運行並超時,出現一些非常緊密的無限循環(例如,「20 PRINT GOTO 10」的Java字節碼等價物),並且我沒有修改正在運行的代碼的選項。

任何想法,爲什麼我看到這種行爲;我能做些什麼來阻止它;或替代方法讓我實現我的目標,運行和超時此類代碼?

回答

4

我發現實際殺死在線程內執行的代碼的唯一方法是使用已棄用的.stop方法。在很多情況下,它被棄用的原因並不重要。

我在clojail中有這個功能。隨意取出該功能或只需拉入庫。

+0

這看起來確實很有用,但是當我這樣做時(thunk-timeout(loop [a 0](recur a))10000)函數只是掛起而CPU上升...即使在10000ms之後也沒有超時。有任何想法嗎? – 2012-07-17 10:48:18

+0

我很快就說過了;當我創建一個ThreadGroup並將它傳遞給你的thunk-timeout時,一個循環函數確實停止了。謝謝:) – 2012-07-17 19:31:40

+0

爲什麼需要ThreadGroup,只需將它包裝在fn中就足夠了:) (thunk-timeout(fn [](loop [a 0](recur a))10000))) – o0omycomputero0o 2015-12-16 03:57:36

1

根據Java API文檔,cancel沒有授予能夠立即停止工作。

試圖取消對此任務的執行。如果任務已完成,已被取消或因其他原因無法取消,此嘗試將失敗。如果成功,並且此任務在調用取消時尚未開始,則此任務不應運行。如果任務已經啓動,則mayInterruptIfRunning參數確定是否執行此任務的線程應該以試圖停止任務被中斷。

6

Java支持線程取消的機制是interrupts。該.stop()方法已被棄用的一個原因 - 見the docs,有效的Java,Java併發實踐等

事實上,FutureTask實施(它備份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()