我想在Clojure中創建一些未來,並在特定的線程上運行它們,以確保它們一次運行一次。這可能嗎?如何在單線程中執行一些Clojure期貨?
將Java庫包裝起來並不難,但在這之前我想確保我不會錯過Clojure的做法。在Java中,我可以通過執行FutureTask
並將這些任務提交給單線程執行程序來執行此操作。
我想在Clojure中創建一些未來,並在特定的線程上運行它們,以確保它們一次運行一次。這可能嗎?如何在單線程中執行一些Clojure期貨?
將Java庫包裝起來並不難,但在這之前我想確保我不會錯過Clojure的做法。在Java中,我可以通過執行FutureTask
並將這些任務提交給單線程執行程序來執行此操作。
Clojure的future macro調用future-call功能使用a dedicated executor service。這意味着您無法控制執行順序執行。
在另一方面,你可以使用的promise代替future
對象,一個future
線程順序deliver結果。 Promise的API與future
提供的相似。他們也有deref和realized?。
以下代碼示例具有在後臺的新線程中按順序執行的子任務,而函數的立即返回結果包含對計算值的承諾。
(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}))
,如果你想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
Manifold提供一種方式來創造未來with specific executor。它不是核心Clojure庫的一部分,但它仍然是一個高質量的庫,如果你需要更多的靈活性來處理未來,而不是核心lib提供的(而不是採用Java interop),那麼這可能是一個最好的選擇。
另外提到的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))
你爲什麼要創造出設計用於併發性,不僅是執行,他們執行的非同時構建?也許你以這種方式定義了現有的任務,或者你通常同時執行它們,但是想在有限的時間內強制順序執行?只是好奇。 – Josh
假設有多個線程(例如,服務器中的請求線程)都希望運行使用共享資源X的任務,而共享資源X一次只能由一個線程使用。一種解決方案是在訪問X時獲取鎖。另一種方法是提交任務(期貨)以在與X關聯的單個線程上運行。其他調用線程可能會或可能不會等待未來的結果。 –
當然,但爲什麼在這種情況下創建線程呢?爲什麼不把這些任務作爲一個線程的函數來調用呢? – Josh