web-dev-qa-db-ja.com

Clojureでスレッドを開始するにはどうすればよいですか?

並行性に関しては、Clojureがいかに優れているかについてたくさん読んだことがありますが、実際に読んだチュートリアルでは、スレッドの作成方法を実際に説明していません。 (.start(Thread。func))だけですか、それとも私が見逃した別の方法がありますか?

43
andrewdotnich

Clojure fnsは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

さらに別のオプションはpcallspmapです。 futureもあります。それらはすべて Clojure API に記載されています。

41
Brian Carper

通常、Clojureでスレッドを開始する場合は、 future を使用します。

使いやすいだけでなく、これには、基になるJavaスレッド化メカニズムにアクセスするために厄介なJava interopを実行する必要がないという利点があります。

使用例:

(future (some-long-running-function))

これにより、別のスレッドで非同期に関数が実行されます。

(def a (future (* 10 10)))

結果を取得したい場合は、未来を逆参照するだけです:

@a
=> 100

@aは、将来のスレッドがその作業を完了するまでブロックすることに注意してください。

32
mikera

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)すべてが完了するまで待機します。タイムアウトする別のバージョンもあります。

14
Carl Smotricz

はい、ClojureでJavaスレッドを開始する方法は、そこにあるものに似ています。

ただし、real質問は次のとおりです。なぜそれをしたいのですか? Clojureには、スレッドよりもmuch優れた同時実行構造があります。

Clojureの標準的な同時実行の例 Rich Hickeyのantコロニーシミュレーション を見ると、使用されているスレッドが正確に0であることがわかります。ソース全体でのJava.lang.Threadへの唯一の参照はThread.sleepへの3つの呼び出しであり、その唯一の目的はシミュレーションを遅くして実際にを参照できるようにすることですUIで何が起こっているか。

すべてのロジックはエージェントで行われます。すべてのアリに1つのエージェント、アニメーションに1つのエージェント、フェロモンの蒸発に1つのエージェントです。競争の場はトランザクションの参照です。スレッドも見えないロックもありません。

9
Jörg W Mittag

ちょうど私の2セント(7年後)を追加します。Clojure関数は、IFnおよびCallableを拡張するRunnableinterface を実装します。したがって、それらをThreadのようなクラスに単に渡すことができます。

プロジェクトですでに core.async を使用している場合は、goマクロを使用することをお勧めします。

(go func)

これはfuncを超軽量で実行します IOC(制御の反転)スレッド

go[...]は、ボディをステートマシンに変換します。ブロッキング操作に到達すると、ステートマシンは「パーク」され、実際の制御スレッドが解放されます。 [...]ブロック操作が完了すると、コードが再開されます[...]

funcがI/Oまたは長時間実行されるタスクを実行する場合は、core.asyncの一部でもあるthreadを使用する必要があります(チェックアウト this 優れていますブログ投稿):

(thread func)

とにかく、Java相互運用構文に固執する場合は、->(スレッド/矢印)マクロ:

(-> (Thread. func) .start)
5
beatngu13

フューチャーを使用することは、通常、スレッディングへの最も簡単なアドホックアクセスです。あなたが何をしたいかに完全に依存します:)

2
Timothy Pratley

(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の作成を回避できます。

1
Lars Bohl