2017-12-27 17 views
0

私はClojureでいくつかの未来を創り出し、それらをすべて特定のスレッドで実行して、それらが1つずつ実行されるようにしたいと思います。これは可能ですか?Clojure先物を1つのスレッドで実行する方法は?

これを行うためにJavaライブラリをラップするのは難しくありませんが、それを行う前に、私はClojureのやり方を忘れていないことを確認したいと思います。 Javaでは、私はFutureTaskを実装し、それらのタスクをシングルスレッドエグゼキュータに提出することでこれを行うことができます。

+0

をなぜあなたは彼らだけが非同時に実行することを強制するために、同時実行のために設計されている構造を作成したいのでしょうか?既存のタスクがこのように定義されているか、通常は同時に実行されますが、限られた時間だけ順次実行する必要がありますか?ちょっと興味があるんだけど。 – Josh

+0

一度に1つのスレッドだけが使用できる共有リソースXを使用するタスクを実行したいと望む複数のスレッド(たとえば、サーバー内の要求スレッド)があるとします。 1つの解決策は、Xにアクセスするたびにロックを取得することです。もう1つの方法は、Xに関連付けられた単一のスレッドで実行するタスク(先物)を送信することです。他の呼び出しスレッドは、将来の結果を待つかもしれません。 –

+0

もちろん、この場合スレッドを作成するのはなぜですか?単なるスレッドから関数を呼び出すだけではどうですか? – Josh

答えて

2

Clojureのfuture macroa dedicated executor serviceを使用する関数future-callを呼び出します。つまり、シーケンシャル実行を強制するコントロールはありません。

一方、futureオブジェクトの代わりにpromiseを使用し、futureスレッドを順番にdeliverのスレッドに使用できます。プロミスのAPIはfutureのものと似ています。彼らはderefrealized?も持っています。

次のコード例では、新しいスレッドでバックグラウンドでサブタスクが順次実行され、関数のすぐ返された結果には計算された値に対する約束が含まれています。

(defn start-async-calc [] 
    (let [f1 (promise) 
     f2 (promise) 
     f3 (promise)] 
    (future 
     (deliver f1 (task-1)) 
     (deliver f2 (task-2)) 
     (deliver f3 (task-3))) 
    {:task1 f1 
    :task2 f2 
    :task3 f3})) 
1

あなたはfutureへの呼び出しをsequentializeしたい場合はこのように、手動でそれを使用することができます:彼らはまだ可能性の異なるスレッドで呼び出されるだろうが、次のものがまで呼び出されません

(do @(future 1) 
    @(future 2) 
    @(future 3)) 

前のことが終わった。これは、@(またはderef関数)によって保証されています。つまり、doフォームを実行するスレッドは、完了する前にprev promiseでブロックされ、次に次のスレッドが生成されます。

あなたは、このようなマクロでそれを飾り立てることができます。

(defmacro sequentialize [& futures] 
    `(do [email protected](map #(list `deref %) futures))) 

user> (let [a (atom 1)] 
     (sequentialize 
     (future (swap! a #(* 10 %))) 
     (future (swap! a #(+ 20 %))) 
     (future (swap! a #(- %)))) 
     @a) 
;;=> -30 

これはマニュアルdoとまったく同じことを行います。

user> (let [a (atom 1)] 
     (sequentialize 
     (future (Thread/sleep 100) 
       (swap! a #(* 10 %))) 
     (future (Thread/sleep 200) 
       (swap! a #(+ 20 %))) 
     (future (swap! a #(- %)))) 
     @a) 
;;=> -30 
0

Manifoldは将来with specific executorを作成するための方法を提供します:a原子への変異は、いくつかのスレッドが長く実行しても、インオーダーであることに注意してください。それはコアClojure libの一部ではありませんが、まだ高品質なlibであり、おそらくコアlibよりも先を扱う柔軟性が必要な場合に最適なオプションです(Java interopに頼らず)。

0

上記のpromisesに加えて、delayを使用することができます。約束は、偶然にそれらを納品できず、futuredelayでは不可能なデッドロックシナリオを作り出すという問題があります。未来と遅延の違いは、作業が実行されるスレッドだけです。将来的には、作業はバックグラウンドで行われ、作業が遅れていると、それを逆参照しようとする最初のスレッドによって作業が行われます。将来のは、約束より良いフィットしているのであれば、あなたはいつものような何かを行うことができます:

(def result-1 (delay (long-calculation-1))) 
(def result-2 (delay (long-calculation-2))) 
(def result-3 (delay (long-calculation-3))) 

(defn run-calcs [] 
    @(future 
    @result-1 
    @result-2 
    @result-3)) 
関連する問題