Clojure мешает! функция - PullRequest
       2

Clojure мешает! функция

4 голосов
/ 08 ноября 2011

Я читаю книгу Фогуса о Радость 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"))

... но он возвращает ноль.Почему?

Ответы [ 3 ]

6 голосов
/ 08 ноября 2011

Итак, вот тот же код, слегка подправленный так, что функция, переданная в 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.

Относительно того, что фактически делает этот код:

  1. Он создает новый фиксированный размерпул потоков, который будет использовать не более ядер на вашей машине + 2 .Этот пул называется *pool* и создается с использованием Java Executor Framework .См. [1] для получения более подробной информации.

  2. Функция dothreads! получает функцию, которая будет вызываться exec-count раз в каждом из потоков thread-count. Таким образом, в приведенном выше примере вы можете ясно видеть, что он вызывается 4 раза на поток (:threads равен 2, а :times равен 4).

  3. Причина, по которой эта функциявозвращает ноль в том, что функция 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)), это всегда вернет продукт.Сумма будет оценена, но она будет за ничего .Так что не делай этого!Если вы хотите добавить более одного выражения к функции, убедитесь, что у всех, кроме последнего, есть побочные эффекты, иначе они будут бесполезны.

  1. Вы также можете заметить, что порядок, в котором вещипечатается асинхронно.Итак, на моей машине, это говорит привет 4 раза, затем возвращает результат функции и затем говорит привет 4 раза.Порядок, в котором выполняются функции, между потоками не определен, однако hellos являются последовательными в каждом потоке (никогда не может быть Hello 3 до Hello 2).Причина последовательности заключается в том, что функция, фактически переданная в пулы потоков, имеет значения #(dotimes [c exec-count] (f c)) и

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

3 голосов
/ 08 ноября 2011

Впоследствии он используется в книге для многократного запуска тестовых функций в нескольких потоках.Само по себе это ничего не иллюстрирует, но используется для демонстрации блокировок, обещаний и других параллельных и параллельных вещей.

1 голос
/ 08 ноября 2011

dotimes, dothreads! и println не являются чистыми функциями: они используются для введения побочных эффектов.Например,

user=> (println 3)
3
nil

Этот фрагмент кода печатает 3 на экране, но возвращает ноль.Точно так же dothreads! полезен для его побочных эффектов, а не его возвращаемого значения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...