2011-11-08 53 views
4

我读Fogus的书上的Clojure的喜悦,并在并行编程章我看到一个函数定义这肯定想说明一些重要的东西,但我无法找出什么。此外,我看不出这是什么功能 - 当我执行,它不会做任何事情:Clojure dothreads!功能

(import '(java.util.concurrent Executors)) 
    (def *pool* (Executors/newFixedThreadPool 
    (+ 2 (.availableProcessors (Runtime/getRuntime))))) 

(defn dothreads! [f & {thread-count :threads 
         exec-count :times 
         :or {thread-count 1 exec-count 1}}] 
    (dotimes [t thread-count] 
    (.submit *pool* #(dotimes [_ exec-count] (f))))) 

我试图以这种方式运行:

(defn wait [] (Thread/sleep 1000)) 
(dothreads! wait :thread-count 10 :exec-count 10) 
(dothreads! wait) 
(dothreads! #(println "running")) 

...但它返回nil。为什么?

回答

6

所以,在这里是相同的代码,以便传递给dothreads!这个函数会得到内部dotimes的数量略有微调。

(import 'java.util.concurrent.Executors) 

(def ^:dynamic *pool* (Executors/newFixedThreadPool (+ 2 (.availableProcessors (Runtime/getRuntime))))) 

(defn dothreads! [f & {thread-count :threads 
         exec-count :times 
         :or {thread-count 1 exec-count 1}}] 
    (dotimes [t thread-count] 
    (.submit *pool* #(dotimes [c exec-count] (f c))))) 

(defn hello [name] 
    (println "Hello " name)) 

尝试运行它像这样:

(dothreads! hello :threads 2 :times 4) 

对于我来说,它打印东西的效果:

Hello 0 
Hello 1 
Hello 2 
Hello 3 
nil 
user=> Hello 0 
Hello 1 
Hello 2 
Hello 3 

所以,注意你调用函数时犯了一个错误:您在通过:线程数:EXEC数作为关键字,而这些其实都是在T绑定他解构了dothreads!内部发生的事情。关键字是开始以一个冒号,:threads:times的话。

至于这是什么代码实际上做:

  1. 它创建最多将使用你的机器+ 2的 芯数的新的固定大小的线程池。该池称为*pool*并使用的Java执行器框架创建。有关更多详细信息,请参见[1]。

  2. dothreads!函数获取将要在每个thread-count线程称为exec-count次功能。所以,在上面的例子中,你可以清楚地看到它被称为每一个线程4倍(:threads为2和:times为4)。

  3. 此函数返回nil的原因是函数dothreads!不返回任何内容。线程池的submit方法在Java中返回void,这意味着它在Clojure中返回nil。如果要在使之成为函数的末尾添加一些其它表达:

    (defn dothreads! [f & {thread-count :threads 
             exec-count :times 
             :or {thread-count 1 exec-count 1}}] 
        (dotimes [t thread-count] 
        (.submit *pool* #(dotimes [c exec-count] (f c)))) 
        (* thread-count exec-count)) 
    

它将返回8上面的例子(2 * 4)。只有函数中的最后一个表达式被返回,所以如果在函数中你要写(fn [x y] (+ x y) (* x y))这将始终返回该产品。总和将被评估,但它将为没有任何。所以,不要这样做!如果你想添加一个表达式到一个函数,确保除了最后一个都有副作用,否则它们就没用了。

  1. 你也可能注意到了,在其中打印东西的顺序是异步的。所以,在我的机器上,它会打4声,然后返回函数的结果,然后再打4声。线程之间执行函数的顺序是不确定的,但是每个线程中的hello都是顺序的(在Hello 2之前永远不会有Hello 3)。究其原因,顺序性是实际提交给线程池的功能是#(dotimes [c exec-count] (f c))

[1] http://download.oracle.com/javase/tutorial/essential/concurrency/executors.html

+0

谢谢您的详细解释!其实,我对你的代码有一点点玩法。第一次,它没有在cljr中输出任何东西:[link](http://i44.tinypic.com/11io8pl.png) 但是 - 足够奇怪 - 如果我从命令提示符运行cljr(cmd )在Windows上,它将消息打印到cmd窗口! 之后,我尝试了一个匿名函数: (dothreads!#(println“Hello”name):threads 2:times 4) ...但它没有工作,这也很奇怪。 – asdfghjkl

+0

我想也许在“cljr-without-cmd”模式下缺少打印输出的原因是println函数被编译成一些Java(?)中的“System.out.println”调用,它想要打印正确到一些控制台输出(?)。默认情况下,cljr以某种方式在评估源代码时“捕获”这些println,但是当另一个Java线程执行打印时,这些线程不知道在哪里写输出......但这是猜测。 – asdfghjkl

+0

现在完全猜测 - 但也许尝试使用Clojure 1.3运行代码?这就是我测试它的原因。我不知道为什么它没有在你的例子中打印任何东西。 错误,如果你接受我的答案,也请勾选它;) –

3

它之后用在书中多次在多个线程中运行测试功能。它本身没有说明任何东西,但是它用来演示锁定,承诺以及其他并行和并发的东西。

+0

没关系,我会阅读! – asdfghjkl

1

dotimesdothreads!println不是纯函数:它们用于引入副作用。例如,

user=> (println 3) 
3 
nil 

该代码片段在屏幕上打印3,但返回nil。同样,dothreads!对其副作用而非其返回值很有用。