Clojure-concurrent-programming

提供:Dev Guides
移動先:案内検索

Clojure-並行プログラミング

Clojureプログラミングでは、ほとんどのデータ型は不変であるため、並行プログラミングに関しては、コードが複数のプロセッサで実行される場合、これらのデータ型を使用するコードは非常に安全です。 しかし、多くの場合、データを共有する必要があり、複数のプロセッサ間で共有されるデータに関しては、複数のプロセッサを使用する場合、整合性に関してデータの状態を維持する必要があります。 これは*並行プログラミング*として知られており、Clojureはそのようなプログラミングのサポートを提供します。

dosync、ref、set、alterなどを通じて公開されるソフトウェアトランザクショナルメモリシステム(STM) 同期的かつ調整された方法でスレッド間で状態の変化を共有することをサポートします。 エージェントシステムは、非同期で独立した方法でスレッド間で状態の変化を共有することをサポートします。 アトムシステムは、同期的かつ独立した方法でスレッド間で状態の変化を共有することをサポートしています。 一方、def、バインディングなどを通じて公開される動的なvarシステム スレッド内の変化する状態の分離をサポートします。

他のプログラミング言語も、並行プログラミングのモデルに従います。

  • 変更可能なデータへの直接参照があります。
  • 共有アクセスが必要な場合、オブジェクトはロックされ、値が変更され、その値への次のアクセスのためにプロセスが続行されます。

Clojureにはロックはありませんが、不変の永続データ構造への間接参照です。

Clojureには3種類の参照があります。

  • Vars -変更はスレッドで分離されます。
  • 参照-変更はスレッド間で同期および調整されます。
  • エージェント-スレッド間の非同期の独立した変更を含みます。

同時プログラミングに関して、Clojureでは次の操作が可能です。

トランザクション

Clojureの同時実行性はトランザクションに基づいています。 参照はトランザクション内でのみ変更できます。 トランザクションには次のルールが適用されます。

  • すべての変更はアトミックで分離されています。
  • 参照へのすべての変更はトランザクションで発生します。
  • トランザクションは、別のトランザクションによる効果を認識しません。
  • すべてのトランザクションはdosyncブロック内に配置されます。

dosyncブロックの機能についてはすでに説明しました。もう一度見てみましょう。

dosync

式とネストされた呼び出しを含むトランザクションで式を(暗黙のdoで)実行します。 このスレッドで既に何も実行されていない場合、トランザクションを開始します。 キャッチされない例外は、トランザクションを中止し、dosyncから流出します。

構文は次のとおりです。

構文

(dosync expression)

パラメータ-「式」は、dosyncブロックに含まれる式のセットです。

戻り値-なし。

参照変数の値を変更しようとする例を見てみましょう。

(ns clojure.examples.example
   (:gen-class))
(defn Example []
   (def names (ref []))
   (alter names conj "Mark"))
(Example)

出力

上記のプログラムを実行すると、次のエラーが発生します。

Caused by: java.lang.IllegalStateException: No transaction running
   at clojure.lang.LockingTransaction.getEx(LockingTransaction.java:208)
   at clojure.lang.Ref.alter(Ref.java:173)
   at clojure.core$alter.doInvoke(core.clj:1866)
   at clojure.lang.RestFn.invoke(RestFn.java:443)
   at clojure.examples.example$Example.invoke(main.clj:5)
   at clojure.examples.example$eval8.invoke(main.clj:7)
   at clojure.lang.Compiler.eval(Compiler.java:5424)
   ... 12 more

エラーから、最初にトランザクションを開始しないと参照タイプの値を変更できないことが明確にわかります。

上記のコードを機能させるには、次のプログラムで行うように、dosyncブロックにalterコマンドを配置する必要があります。

(ns clojure.examples.example
   (:gen-class))
(defn Example []
   (def names (ref []))

   (defn change [newname]
      (dosync
         (alter names conj newname)))
   (change "John")
   (change "Mark")
   (println @names))
(Example)

上記のプログラムは、次の出力を生成します。

出力

[John Mark]

dosyncの別の例を見てみましょう。

(ns clojure.examples.example
   (:gen-class))
(defn Example []
   (def var1 (ref 10))
   (def var2 (ref 20))
   (println @var1 @var2)

   (defn change-value [var1 var2 newvalue]
      (dosync
         (alter var1 - newvalue)
         (alter var2 + newvalue)))
   (change-value var1 var2 20)
   (println @var1 @var2))
(Example)

上記の例では、dosyncブロックで変更される2つの値があります。 トランザクションが成功すると、両方の値が変更されます。そうでない場合、トランザクション全体が失敗します。

上記のプログラムは、次の出力を生成します。

出力

10 20
-10 40