並行性に関しては、Clojureがいかに優れているかについてたくさん読んだことがありますが、実際に読んだチュートリアルでは、スレッドの作成方法を実際に説明していません。 (.start(Thread。func))だけですか、それとも私が見逃した別の方法がありますか?
Clojure fn
sはRunnable
であるため、投稿したとおりに使用するのが一般的です。
user=> (dotimes [i 10] (.start (Thread. (fn [] (println i)))))
0
1
2
4
5
3
6
7
8
9
nil
別のオプションは agents を使用することです。この場合、send
またはsend-off
そしてプールからのスレッドを使用します。
user=> (def a (agent 0))
#'user/a
user=> (dotimes [_ 10] (send a inc))
nil
;; ...later...
user=> @a
10
さらに別のオプションはpcalls
とpmap
です。 future
もあります。それらはすべて Clojure API に記載されています。
通常、Clojureでスレッドを開始する場合は、 future を使用します。
使いやすいだけでなく、これには、基になるJavaスレッド化メカニズムにアクセスするために厄介なJava interopを実行する必要がないという利点があります。
使用例:
(future (some-long-running-function))
これにより、別のスレッドで非同期に関数が実行されます。
(def a (future (* 10 10)))
結果を取得したい場合は、未来を逆参照するだけです:
@a
=> 100
@aは、将来のスレッドがその作業を完了するまでブロックすることに注意してください。
Programming Clojureは、167ページの「非同期更新にエージェントを使用する」までその質問に対処しません。
スレッドを開始する前に、Clojureは半分のチャンスで、それ自体でマルチタスクを実行することに注意してください。私は陽気に並行性を無視したプログラムを作成しましたが、条件が正しければ、複数のCPUを占有することがわかりました。これはそれほど厳密な定義ではないことを知っています。これについてはまだ詳しく説明していません。
しかし、明示的に別個のアクティビティが本当に必要な場合には、Clojureの答えの1つが明らかにエージェントです。
(agent initial-state)
作成します。 Javaスレッドは、実行を待機しているコードブロックであるという意味ではありません。代わりに、実行する作業を待機しているアクティビティです。これは、
(send agent update-fn & args)
例では
(def counter (agent 0))
counter
はエージェントの名前とハンドルです。エージェントの状態は数値0です。
これを設定したら、作業をエージェントに送信できます。
(send counter inc)
与えられた関数をその状態に適用するように伝えます。
後でそれを逆参照することで、エージェントから状態を引き出すことができます。
@counter
は、0から始まった数値の現在の値を示します。
関数await
を使用すると、長いアクティビティであれば、エージェントのアクティビティに対してjoin
のようなことができます。
(await & agents)
すべてが完了するまで待機します。タイムアウトする別のバージョンもあります。
はい、ClojureでJavaスレッドを開始する方法は、そこにあるものに似ています。
ただし、real質問は次のとおりです。なぜそれをしたいのですか? Clojureには、スレッドよりもmuch優れた同時実行構造があります。
Clojureの標準的な同時実行の例 Rich Hickeyのantコロニーシミュレーション を見ると、使用されているスレッドが正確に0であることがわかります。ソース全体でのJava.lang.Thread
への唯一の参照はThread.sleep
への3つの呼び出しであり、その唯一の目的はシミュレーションを遅くして実際にを参照できるようにすることですUIで何が起こっているか。
すべてのロジックはエージェントで行われます。すべてのアリに1つのエージェント、アニメーションに1つのエージェント、フェロモンの蒸発に1つのエージェントです。競争の場はトランザクションの参照です。スレッドも見えないロックもありません。
ちょうど私の2セント(7年後)を追加します。Clojure関数は、IFn
およびCallable
を拡張するRunnable
interface を実装します。したがって、それらをThread
のようなクラスに単に渡すことができます。
プロジェクトですでに core.async を使用している場合は、go
マクロを使用することをお勧めします。
(go func)
これはfunc
を超軽量で実行します IOC(制御の反転)スレッド :
go[...]は、ボディをステートマシンに変換します。ブロッキング操作に到達すると、ステートマシンは「パーク」され、実際の制御スレッドが解放されます。 [...]ブロック操作が完了すると、コードが再開されます[...]
func
がI/Oまたは長時間実行されるタスクを実行する場合は、core.asyncの一部でもあるthread
を使用する必要があります(チェックアウト this 優れていますブログ投稿):
(thread func)
とにかく、Java相互運用構文に固執する場合は、->
(スレッド/矢印)マクロ:
(-> (Thread. func) .start)
フューチャーを使用することは、通常、スレッディングへの最も簡単なアドホックアクセスです。あなたが何をしたいかに完全に依存します:)
(future f)
マクロは、フォームfを(fn *を介して)Callableにラップし、すぐにスレッドプールに送信します。
たとえば、Java.lang.ThreadオブジェクトをJava.lang.Runtimeシャットダウンフックとして使用するための参照が必要な場合は、次のようなスレッドを作成できます。
(proxy [Thread] [] (run [] (println "running")))
これはまだスレッドを開始せず、作成するだけです。スレッドを作成して実行するには、スレッドプールに送信するか、.startを呼び出します。
(->
(proxy [Thread] [] (run [] (println "running")))
(.start))
Briansの答えもスレッドを作成しますが、プロキシを必要としないため、非常にエレガントです。一方、プロキシを使用すると、Callableの作成を回避できます。