在 ExecutorService 中休眠线程 (Java/Clojure)

发布于 2024-10-25 07:20:22 字数 616 浏览 1 评论 0原文

我在 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 的争论?如果我理解正确的话,在这种情况下,我的每个工作人员都有自己的线程,因此应该不会有副作用。

如果执行器使用固定线程池:

(Executors/newFixedThreadPool 1000)

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

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

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

I have a rather massive number of threads being created inside a clojure program:

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

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

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

It's been a while between JVMs for me and I am basically wondering here if there is any argument against using sleep or yield inside the function that is being executed by the Executor? If I understand correctly, in this case, every one of my workers has it's own thread and therefore there should be no side effects.

If the Executor is using a FixedThreadPool:

(Executors/newFixedThreadPool 1000)

Things become more complicated because threads will not be returned to the pool until their work is complete, meaning the other queued workers will take longer to complete if the threads are sleeping.

Is my understanding of threading in this instance correct?

(Note: I suspect my design is actually wrong, but just want to make sure I am on the right page)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

眼眸 2024-11-01 07:20:22

执行器在概念上是一个任务队列+一个工作池。您对此处将发生的情况的解释基本上是正确的。当您向执行程序提交任务时,工作将排队,直到线程可以执行该任务。当它执行任务时,该任务拥有该线程,并且睡眠将阻止其他任务在该工作线程上执行。

根据您正在做的事情,这可能没问题(尽管在任务中睡觉是不寻常的,而且可能是不好的形式)。更常见的是,阻塞线程是等待 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.

An executor is conceptually a task queue + a worker pool. Your explanation of what will happen here is basically correct. When you submit a task to the executor, the work is enqueued until a thread can execute the task. When it is executing the task, that task owns the thread and sleeping will block other tasks from being executed on that worker thread.

Depending on what you're doing that may be ok (although it is unusual and probably bad form to sleep inside a task). It's more common to block a thread as a side effect of waiting on IO (blocked on a socket or db call for example).

Generally if you are doing periodic work, it is better to handle that outside the pool and fire tasks when they should be executed, or better yet, use a ScheduledExecutorService instead from Executors/newScheduledThreadPool.

The other main mechanism in Java for performing time-based tasks is java.util.Timer, which is a bit easier to use but not as robust as the ScheduledExecutorService.

Another alternative from Clojure is to explicitly put the worker into a background thread managed by Clojure instead of by you:

(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.
彼岸花ソ最美的依靠 2024-11-01 07:20:22

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

An alternative to sleeping your threads is to have each worker have a "sleepUntil" long value. When your executor calls a worker, if it is sleeping it returns immediately. Otherwise, it does its work, then returns. This can help keep your thread count down, because a FixedThreadPoolExecutor will be able to handle many more workers than it has threads, if most of them are flagged as sleeping and return quickly.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文