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 が FixedThreadPool を使用している場合:

(Executors/newFixedThreadPool 1000)

スレッドは作業が完了するまでプールに返されないため、状況はさらに複雑になります。つまり、スレッドがスリープ状態の場合、キューに入れられた他のワーカーの完了に時間がかかります。

この場合のスレッド化についての私の理解は正しいですか?

(注:私のデザインは実際には間違っていると思いますが、正しいページにいることを確認したいだけです)

4

2 に答える 2

7

エグゼキューターは、概念的にはタスク キュー + ワーカー プールです。ここで何が起こるかについてのあなたの説明は基本的に正しいです。タスクをエグゼキュータに送信すると、スレッドがタスクを実行できるようになるまで作業がキューに入れられます。タスクを実行しているとき、そのタスクはスレッドを所有し、スリープすると、そのワーカー スレッドで他のタスクが実行されなくなります。

何をしているかによっては、それで問題ないかもしれません (ただし、タスク内でスリープするのは珍しいことであり、おそらく悪い形です)。IO を待機することの副作用として、スレッドをブロックする方が一般的です (たとえば、ソケットまたは db 呼び出しでブロックされます)。

通常、定期的な作業を行っている場合は、プールの外でそれを処理し、実行する必要があるときにタスクを起動するか、Executors/newScheduledThreadPool の代わりに ScheduledExecutorServiceを使用することをお勧めします。

時間ベースのタスクを実行するための Java のもう 1 つの主なメカニズムはjava.util.Timerです。これは少し使いやすいですが、ScheduledExecutorService ほど堅牢ではありません。

Clojure のもう 1 つの方法は、ユーザーではなく 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」の long 値を持たせることもできます。エグゼキュータがワーカーを呼び出すと、スリープ中の場合はすぐに戻ります。それ以外の場合は、その作業を行ってから戻ります。ほとんどのワーカーにスリープ状態のフラグが立てられ、すぐに戻る場合、FixedThreadPoolExecutor はスレッドよりも多くのワーカーを処理できるため、これはスレッド数を抑えるのに役立ちます。

于 2011-03-22T21:25:16.790 に答える