Итак, вот тот же код, слегка подправленный так, что функция, переданная в 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-count в качестве ключей, тогда как на самом деле они являются связыванием в деструктуризации, происходящей внутри dothreads!
.Ключевыми словами являются слова, начинающиеся с двоеточия, :threads
и :times
.
Относительно того, что фактически делает этот код:
Он создает новый фиксированный размерпул потоков, который будет использовать не более ядер на вашей машине + 2 .Этот пул называется *pool*
и создается с использованием Java Executor Framework .См. [1] для получения более подробной информации.
Функция dothreads!
получает функцию, которая будет вызываться exec-count
раз в каждом из потоков thread-count
. Таким образом, в приведенном выше примере вы можете ясно видеть, что он вызывается 4 раза на поток (:threads
равен 2, а :times
равен 4).
Причина, по которой эта функциявозвращает ноль в том, что функция dothreads!
ничего не возвращает.Метод submit
пула потоков возвращает void в Java, и это означает, что он возвращает nil в Clojure.Если вы добавите какое-то другое выражение в конец функции, сделав его следующим:
(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 раза.Порядок, в котором выполняются функции, между потоками не определен, однако hellos являются последовательными в каждом потоке (никогда не может быть Hello 3 до Hello 2).Причина последовательности заключается в том, что функция, фактически переданная в пулы потоков, имеет значения
#(dotimes [c exec-count] (f c))
и
[1] http://download.oracle.com/javase/tutorial/essential/concurrency/executors.html