我正在阅读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"))
...但它返回零。为什么呢?
答案 0 :(得分: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
所以,请注意你在调用函数时犯的一个错误:你传入: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))
上面的示例(2 * 4)将返回8。只返回函数中的最后一个表达式,因此如果在函数中写入(fn [x y] (+ x y) (* x y))
,则将始终返回该产品。总和将被评估,但它将是 nothing 。所以,不要这样做!如果你想在函数中添加更多的那个表达式,请确保除了最后一个表达式之外的所有表达式都有副作用,否则它们将无用。
#(dotimes [c exec-count] (f c))
和[1] http://download.oracle.com/javase/tutorial/essential/concurrency/executors.html
答案 1 :(得分:3)
之后在本书中使用它在多个线程中多次运行测试函数。它本身并没有说明任何东西,但它用于演示锁定,承诺以及其他并行和并发的东西。
答案 2 :(得分:1)
dotimes
,dothreads!
和println
不是纯函数:它们用于引入副作用。例如,
user=> (println 3)
3
nil
该代码段在屏幕上打印3,但返回nil。同样,dothreads!
对其副作用很有用,而不是它的返回值。