2011-03-22 60 views
5

我有一個Clojure的程序中創建線程,而數量龐大的內螺紋:睡一個ExecutorService(Java/Clojure的)

(import '(java.util.concurrent Executors)) 
(def *pool* 
    (Executors/newCachedThreadPool)) 

(defn do-something [] 
    ; work 
    Thread/sleep 200 
    ; repeat) 

(dotimes [i 10000] 
    (.submit *pool* do-something)) 

它已經JVM之間一段時間我和我基本上是想在這裏如果在Executor正在執行的函數內部有任何反對使用睡眠或yield的參數?如果我理解正確,在這種情況下,我的每一個工人都有自己的線程,因此應該沒有副作用。

如果執行程序使用FixedThreadPool:

(Executors/newFixedThreadPool 1000) 

事情變得更加複雜,因爲直到他們的工作完成線程不會返回到池中,這意味着其他排隊的工人將需要更長的時間才能完成,如果線程正在睡覺。

我對這個實例中線程的理解是否正確?

(注:我懷疑我的設計其實是錯誤的,而只是想確保我在正確的頁面上)

回答

7

執行者在概念上是任務隊列+工作者池。你對這裏發生的事情的解釋基本上是正確的。當您向執行者提交任務時,工作將被排隊,直到線程可以執行任務。當它執行任務時,該任務擁有該線程,並且睡眠將阻止其他任務在該工作線程上執行。

取決於你在做什麼,可能是好的(雖然這是一種不尋常的,可能是不好的形式睡在一個任務內)。阻塞線程作爲等待IO的副作用(例如,在套接字或數據庫調用上被阻塞)會更常見。

通常,如果您正在進行定期工作,最好在執行池之外處理並執行任務,或者更好地使用ScheduledExecutorService而不是Executors/newScheduledThreadPool。

Java中用於執行基於時間的任務的另一個主要機制是java.util.Timer,這有點容易使用但不像ScheduledExecutorService那樣健壯。

從Clojure的另一種選擇是明確提出工人到由Clojure的管理後臺線程,而不是由你:

(defn do-task [] 
    (println (java.util.Date.) "doing task")) 

(defn worker [f n wait] 
      (doseq [task (repeat n f)] 
        (f) 
        (Thread/sleep wait))) 

;; use future to execute worker in a background thread managed by Clojure 
(future (worker do-task 10 1000)) 

;; the call to future returns immediately but in the background console 
;; you will see the tasks being run. 
-1

睡你的線程另一種方法是讓每個員工都有一個「sleepUntil」長期價值。當您的執行者呼叫工人時,如果它正在睡覺,它會立即返回。否則,它會完成工作,然後返回。這可以幫助保持你的線程數量減少,因爲一個FixedThreadPoolExecutor將能夠處理比線程更多的工作者,如果他們中的大多數被標記爲睡眠並快速返回。