6

我在 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 正在执行的函数中使用 sleep 或 yield 的论点?如果我理解正确,在这种情况下,我的每个工人都有自己的线程,因此应该没有副作用。

如果 Executor 使用的是 FixedThreadPool:

(Executors/newFixedThreadPool 1000)

事情变得更加复杂,因为线程在工作完成之前不会返回到池中,这意味着如果线程处于睡眠状态,其他排队的工作人员将需要更长的时间才能完成。

在这种情况下,我对线程的理解是否正确?

(注意:我怀疑我的设计实际上是错误的,但只是想确保我在正确的页面上)

4

2 回答 2

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.
于 2011-03-22T21:29:36.567 回答
-1

休眠线程的另一种方法是让每个工作人员都有一个“sleepUntil”长值。当你的执行者调用一个工人时,如果它正在睡觉,它会立即返回。否则,它会完成它的工作,然后返回。这可以帮助减少线程计数,因为 FixedThreadPoolExecutor 将能够处理比它拥有的线程更多的工作线程,如果它们中的大多数被标记为睡眠并快速返回。

于 2011-03-22T21:25:16.790 回答