2009-11-20 308 views

回答

37

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,在這種情況下,你會sendsend-off,它會使用一個線程池。

user=> (def a (agent 0)) 
#'user/a 
user=> (dotimes [_ 10] (send a inc)) 
nil 
;; ...later... 
user=> @a 
10 

另一種選擇是pcallspmap。還有future。他們都記錄在Clojure API

+1

啊,是的,代理機制是我忘記的東西。謝謝! – andrewdotnich 2009-11-20 14:52:44

+2

不要忘了pmap! – Dan 2009-11-26 13:29:23

+0

「Clojure fns是可運行的,所以通常按照您發佈的方式使用它們,是的。」非常感謝您提供這些信息。 – sjas 2013-12-29 19:31:28

7

是的,您在Clojure中啓動Java線程的方式就像您在那裏擁有的東西。

但是,真實的問題是:你爲什麼要這麼做? Clojure擁有比線程更好的併發結構。

如果您看一下Clojure中的規範併發示例,Rich Hickey's ant colony simulation,您將看到它恰好使用0個線程。整個數據源中對java.lang.Thread的唯一引用是對Thread.sleep的三次調用,其唯一目的是減慢模擬速度,以便您實際上可以請參閱 UI中正在進行的操作。

所有的邏輯都在代理中完成:每個螞蟻一個代理,一個動畫代理和一個信息素蒸發代理。比賽場地是交易參考。不是一個線程或鎖定在視線內。

+8

這是不正確的。 Clojure使用當然線程下的線程。但它提供了同步和協調這些線程的抽象,例如。期貨,pmap或代理商。然後出現問題,你想用java.util.concurrent機制來使用線程。 – kotarak 2009-11-20 06:39:09

+4

我想給Jörg帶來這樣的疑問:當他說線程時,他的意思是線程API,但這應該清楚。 – 2009-11-20 06:47:59

+9

Clojure在其內部運行時使用什麼來實現代理,期貨以及Rich Hickey的業務,而不是我的業務。事實上,Clojure的JVM版本恰好使用了線程池,這與語言語義完全無關。 Tomorrow Rich可能會改變主意並將它們實現爲Continuations,Clojure的.NET版本可能會將它們實現爲「任務」,ClojureScript(Clojure的JavaScript版本)可能將它們實現爲HTML5 Web Workers,並假設未來Erlang託管版本可能將它們實現爲Actor。作爲Clojure用戶,我永遠不會知道。 – 2009-11-20 07:08:20

14

編程Clojure直到頁167:「使用代理進行異步更新」才解決該問題。

在你開始線程之前,請注意Clojure將自己完成多任務,只要有一半機會。我已經寫出了對併發性無知的程序,並發現當條件合適時,它們佔用多個CPU。我知道這不是一個非常嚴格的定義:我還沒有深入探討這一點。

但是對於那些你確實需要明確的單獨活動的場合,Clojure的答案之一顯然是代理人。

(agent initial-state)

將創建一個。它不像是一個等待執行的代碼塊的Java線程。相反,這是一項等待開展工作的活動。你這樣做通過

(send agent update-fn & args)

的例子並

(def counter (agent 0))

counter是你的名字,並辦理代理;代理的狀態是具有設置了數字0

,你可以把工作交給代理人:

(send counter inc)

會告訴它給定函數應用到它的狀態。

以後,您可以通過取消引用它拉出來的狀態的代理:

@counter會給你,在0開始走出數的當前值。

功能await會讓你這樣做對代理的活動join,它應該是一個漫長的:

(await & agents)將等待,直到他們全部完成;還有另一個版本需要超時。

2

使用未來通常是最簡單的adhoc訪問線程。完全取決於你想要做什麼:)

30

通常,當我想在Clojure中啓動一個線程時,我只需使用future

除了使用簡單之外,這樣做的好處是可以避免執行任何繁瑣的Java互操作來訪問底層的Java線程機制。

實例:

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

這將在另一個線程異步執行該功能。

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

如果你想要得到的結果,只是取消引用的將來,e.g:

@a 
=> 100 

注意@a將阻塞,直到未來的線程已完成其工作。

+0

太美了:'3 – d9k 2014-10-06 12:16:11

+0

另外請注意,除非您對未來有所反應,否則不會拋出異常。請參閱https:// stuartsierra。COM/2015/5月27日/ Clojure中,未捕獲的例外 – jwhitlark 2017-09-28 19:04:15

0

(future f)宏在Callable(通過fn *)封裝了表單f並將其立即提交給線程池。

,如果你需要一個java.lang.Thread中的對象的引用,例如,使用它作爲一個java.lang.Runtime中的關閉掛鉤,您可以創建這樣一個主題:

(proxy [Thread] [] (run [] (println "running"))) 

這不會啓動線程,只創建它。 創建和運行線程,將其提交給一個線程池,或致電。開始它:

(-> 
(proxy [Thread] [] (run [] (println "running"))) 
(.start)) 

Brians的回答也創建一個線程,但並不需要代理,所以這是非常優雅。另一方面,通過使用代理,我們可以避免創建一個Callable。

2

只需添加我的兩美分(7年後):Clojure功能實現IFninterface,延伸Callable以及Runnable。因此,您可以簡單地將它們傳遞給像Thread這樣的類。

如果你的項目可能已經使用core.async,我喜歡使用go宏:

(go func) 

這在執行func超輕IOC (inversion of control) thread

[...]會把身體變成一個狀態機。在達到任何阻塞操作時,狀態機將被「停放」並且實際的控制線程將被釋放。 [...]當阻塞操作完成後,該代碼將恢復[...]

如果func會做I/O或需要長時間運行的任務,你應該使用thread這也是core.async的部分(檢查this優秀博客文章):

(thread func) 

無論如何,如果你想堅持到Java互操作的語法,請考慮使用->(線程/箭頭)宏:

(-> (Thread. func) .start) 
相關問題