2017-12-27 1381 views
0

我想在Clojure中創建一些未來,並在特定的線程上運行它們,以確保它們一次運行一次。這可能嗎?如何在單線程中執行一些Clojure期貨?

將Java庫包裝起來並不難,但在這之前我想確保我不會錯過Clojure的做法。在Java中,我可以通過執行FutureTask並將這些任務提交給單線程執行程序來執行此操作。

+0

你爲什麼要創造出設計用於併發性,不僅是執行,他們執行的非同時構建?也許你以這種方式定義了現有的任務,或者你通常同時執行它們,但是想在有限的時間內強制順序執行?只是好奇。 – Josh

+0

假設有多個線程(例如,服務器中的請求線程)都希望運行使用共享資源X的任務,而共享資源X一次只能由一個線程使用。一種解決方案是在訪問X時獲取鎖。另一種方法是提交任務(期貨)以在與X關聯的單個線程上運行。其他調用線程可能會或可能不會等待未來的結果。 –

+0

當然,但爲什麼在這種情況下創建線程呢?爲什麼不把這些任務作爲一個線程的函數來調用呢? – Josh

回答

2

Clojure的future macro調用future-call功能使用a dedicated executor service。這意味着您無法控制執行順序執行。

在另一方面,你可以使用的promise代替future對象,一個future線程順序deliver結果。 Promise的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

,如果你想sequentialize到future來電您可以手動使用它像這樣:

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

他們仍然可能被稱爲在不同的線程,但下一個會不會被調用,直到之前已完成。這由@(或deref函數)保證。這意味着執行do表單的線程在完成之前將被prev承諾阻塞,然後產生下一個。

你可以用宏美化這樣的:

(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。請注意,突變a原子是有序即使某些線程運行的時間更長:

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。它不是核心Clojure庫的一部分,但它仍然是一個高質量的庫,如果你需要更多的靈活性來處理未來,而不是核心lib提供的(而不是採用Java interop),那麼這可能是一個最好的選擇。

0

另外提到的promises,您可以使用delay。承諾的問題是您可能無意中無法提供它們,並創建一個死鎖場景,這是future s和delay s不可能實現的。未來和延遲之間的差異只是工作執行的線索。未來,這項工作是在後臺完成的,並且延遲工作是由第一個嘗試解除它的線程完成的。因此,如果未來的是一個比承諾更合適,你總是可以這樣做:

(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))