Спящая нить внутри ExecutorService (Java / Clojure) - PullRequest
5 голосов
/ 23 марта 2011

У меня довольно большое количество потоков, создаваемых внутри программы 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 для меня, и я в основном задаюсь вопросом здесь, есть ли какой-либо аргумент против использования sleep или yield внутри функции, которая выполняется Исполнителем? Если я правильно понимаю, в этом случае у каждого из моих работников есть свой собственный поток, и поэтому не должно быть никаких побочных эффектов.

Если Исполнитель использует FixedThreadPool:

(Executors/newFixedThreadPool 1000)

Все усложняется, потому что потоки не будут возвращаться в пул, пока их работа не будет завершена, а это означает, что другим работникам, находящимся в очереди, потребуется больше времени для завершения, если потоки спят.

Правильно ли мое понимание потоков в этом случае?

(Примечание: я подозреваю, что мой дизайн действительно неправильный, но я просто хочу убедиться, что я на правильной странице)

Ответы [ 2 ]

7 голосов
/ 23 марта 2011

Исполнитель - это концептуально очередь задач + рабочий пул. Ваше объяснение того, что здесь произойдет, в основном верно. Когда вы отправляете задачу исполнителю, работа ставится в очередь до тех пор, пока поток не сможет выполнить задачу. Когда она выполняет задачу, эта задача владеет потоком, и спящий режим заблокирует выполнение других задач в этом рабочем потоке.

В зависимости от того, что вы делаете, это может быть нормально (хотя это необычно и, вероятно, плохо спать внутри задачи). Чаще всего блокируют поток как побочный эффект ожидания ввода-вывода (например, блокируется при вызове сокета или db).

Обычно, если вы выполняете периодическую работу, лучше обрабатывать ее вне пула и запускать задачи, когда они должны быть выполнены, или, что еще лучше, использовать 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.
0 голосов
/ 23 марта 2011

Альтернативой для сна ваших потоков является предоставление каждому работнику длинного значения sleepUntil.Когда ваш исполнитель вызывает работника, если он спит, он немедленно возвращается.В противном случае он выполняет свою работу, а затем возвращается.Это может помочь уменьшить счет вашего потока, потому что FixedThreadPoolExecutor сможет обрабатывать гораздо больше рабочих, чем имеет потоки, если большинство из них помечены как спящие и быстро возвращаются.

...