読者です 読者をやめる 読者になる 読者になる

本当は怖い情報科学

情報系大学院生の趣味&実益ブログ。

Clojure記事紹介<百日修行>(4):「PulsarでActorプログラミング」

Clojure記事紹介<百日修行>では、英文で書かれたClojureの記事を要約してひたすら紹介していきます。モットーは「質より量」です。

紹介記事

レベル:中級

Pulsarを用いた、Actorモデルを使った並行プログラミングの紹介記事です。

取り上げられているライブラリ:

Actorモデルで並行プログラミング

Pulsarは、JavaのQuasarというライブラリのClojureバインディングで、Actorモデルを実現するライブラリです。

Actorモデルは、並行処理を記述するためのプログラミングモデルの1つで、Actorと呼ばれる互いに独立した(データを共有しない)プロセスがメッセージをやりとりすることによって処理を進めていきます。Erlang/Elixirといった言語や、Scala/JavaのAkkaなどが有名でしょう。

Actorモデルの利点は、 * 各Actorは共有データを持たないため、データの競合が起きない(起きにくい) * Erlang/Elixirでは、各Actorがメモリ管理などの面で独立しているため、エラー・障害に強い * 計量スレッドやグリーンスレッドで実装されることによって、通信・ディスクアクセスなどのIOを大量に行う際の並列性が確保でき、またレイテンシが隠蔽できるためスループットが向上する

といった物があります。逆にデメリットとしては、

  • 規模が大きくなると、多数のActorを管理するのはプログラミング上難しい
  • どれくらいのActorが並行動作できるかどうかは実装次第
  • 計量スレッドをサポートしていないプラットフォームでは実装がトリッキーになりがちで、制限が多いこともある(JVMとか)

あるかと思います。(私はプログラミング言語の専門ではないので詳しくないです)

Pulsarでプログラミング

この記事では、サンプルとして、非常に簡易なチャットシステムのようなものを作製しています。サーバーの方だけソースコードを転載するので、リンク先でクライアント側のソースも是非参照してください。

各Actorは、受信したメッセージによって場合分けをする、イベントループのような構造の関数を定義します。ソースコード中のreceiveと呼ばれる式がそうです。

(defn- get-name [ref clients]
  (-> (first (filter #(= (:ref %) ref) clients))
      :name))

(defn- broadcast [msg clients]
  (doseq [c clients]
    (! (:ref c) msg)))

(defsfn server [clients]
  (receive
   [:join ref name] (do
                      (link! ref)
                      (broadcast
                       [:info (str name " joined the chat")]
                       clients)
                      (recur (conj clients {:name name :ref ref})))
   [:send ref msg]  (do
                      (broadcast
                       [:new-msg (get-name ref clients) msg]
                       clients)
                      (recur clients))
   [:exit _ ref _]  (let [updated-clients (remove #(= (:ref %) ref) clients)]
                      (broadcast
                       [:info (str (get-name ref clients) " left the chat")]
                       updated-clients)
                      (recur updated-clients))
   :shutdown        (println "Shutting down")))

(defn create-server []
  (spawn :trap true server '()))

個人的な感想としては、ちょっと記法にクセがあるのが気になるのと、これならcore.asyncでよくね?という気がします。

Clojure Applied: From Practice to Practitioner

Clojure Applied: From Practice to Practitioner

プログラミングClojure 第2版

プログラミングClojure 第2版

【広告】