我正在阅读Fogus的《 Clojure的喜悦》一书,在并行编程一章中,我看到了一个函数定义,该函数定义当然想说明一些重要的东西,但我找不到。而且,我看不到此函数的作用-执行时,它什么也没做:

(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。为什么?

最佳答案

因此,这里是相同的代码,稍作调整,以便传递给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


因此,请注意您在调用函数时犯的一个错误:您将:thread-count和:exec-count作为键传递,而实际上是dothreads!中发生的解构中的绑定。关键字是以冒号,:threads:times开头的单词。

关于这段代码的实际作用:


它创建一个新的固定大小的线程池,最多将使用
计算机中的内核数+2。此池称为*pool*,是使用Java Executor Framework创建的。有关更多详细信息,请参见[1]。
dothreads!函数获得一个在每个exec-count线程上都会被调用thread-count次的函数。因此,在上面的示例中,您可以清楚地看到每个线程被调用4次(:threads为2,:times为4)。
该函数返回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)),则将始终返回乘积。该总和将被评估,但将一事无成。所以,不要这样做!如果要向一个函数添加多个表达式,请确保除最后一个表达式之外的所有表达式都有副作用,否则它们将无用。


您可能还会注意到,打印材料的顺序是异步的。因此,在我的机器上,它会问好4次,然后返回该函数的结果,然后再问问4次。线程之间的执行顺序不确定,但是在每个线程中问候是顺序的(在Hello 2之前永远不会有Hello 3)。顺序的原因是实际提交给线程池的函数是#(dotimes [c exec-count] (f c))


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

08-06 14:58