六 月
28
水曜日

send関数とsend-off関数の違い

Clojureのsend関数とsend-off関数。機能は全く同じのこの二つの関数の違いを確認する。


APIドキュメント

send

Usage: (send a f & args)
Dispatch an action to an agent. Returns the agent immediately.
Subsequently, in a thread from a thread pool, the state of the agent
will be set to the value of:

send-off

Usage: (send-off a f & args)
Dispatch a potentially blocking action to an agent. Returns the
agent immediately. Subsequently, in a separate thread, the state of
the agent will be set to the value of:

ブロッキングされる可能性がある処理は send-off を使えとの事。
具体的に何が違うのかソースを読んでみる。

ソースコード

core/send

(defn send
  "Dispatch an action to an agent. Returns the agent immediately.
  Subsequently, in a thread from a thread pool, the state of the agent
  will be set to the value of:
  (apply action-fn state-of-agent args)"
  {:added "1.0"
   :static true}
  [^clojure.lang.Agent a f & args]
  (apply send-via clojure.lang.Agent/pooledExecutor a f args))

core/send-off

(defn send-off
  "Dispatch a potentially blocking action to an agent. Returns the
  agent immediately. Subsequently, in a separate thread, the state of
  the agent will be set to the value of:

  (apply action-fn state-of-agent args)"
  {:added "1.0"
   :static true}
  [^clojure.lang.Agent a f & args]
  (apply send-via clojure.lang.Agent/soloExecutor a f args))

両関数ともに send-viaのラッパーで 違いは利用するExecutorServiceのみ。

sendclojure.lang.Agent/pooledExecutor を、send-offclojure.lang.Agent/soloExecutor をデフォルトで利用している。
そしてデフォルトは set-agent-send-executor! set-agent-send-off-executor!で変更する事ができる。

clojure.lang.Agent/pooledExecutor

volatile public static ExecutorService pooledExecutor =
  Executors.newFixedThreadPool(2 + Runtime.getRuntime().availableProcessors(), 
      createThreadFactory("clojure-agent-send-pool-%d", sendThreadPoolCounter));

send が利用する pooledExecutor はプロセッサ数 + 2の固定のExecutorServiceを利用する。
CPU数に準じる固定数であるため、CPUバウンドな処理に向いている。

clojure.lang.Agent/soloExecutor

volatile public static ExecutorService soloExecutor = Executors.newCachedThreadPool(
  createThreadFactory("clojure-agent-send-off-pool-%d", sendOffThreadPoolCounter));

一方 send-off が利用する soloExecutor は可変のExecutorServiceを利用する。
可変(最大Integer.MAX_VALUE)であるため必要な分だけプールを広げてスレッドを起こすことができるためIOバウンドな処理に向いている。
また処理後の空きスレッドは60秒以内であれば再利用され60秒以上経つと破棄されてプールは縮小する。

まとめ

  • send, send-offの違いは利用するExecutorService。
  • ブロッキングする処理はsend-offを利用する。
  • send-viaを関数を利用すると任意のExecutorServiceを利用することができる。
  • send が利用するExecutorServiceは set-agent-send-executor! で変更可能。
  • send-off が利用するExecutorServiceは set-agent-send-off-executor! で変更可能。