Я много читал о том, насколько велика Clojure, когда дело доходит до concurrency, но ни один из обучающих программ, которые я прочитал, на самом деле не объясняет, как создать поток. Вы просто делаете (.start(Thread. Func)), или есть другой способ, который я пропустил?
Как запустить поток в Clojure?
Ответ 1
Clojure fn
являются Runnable
, поэтому он распространен, чтобы использовать их точно так же, как вы опубликовали, да.
user=> (dotimes [i 10] (.start (Thread. (fn [] (println i)))))
0
1
2
4
5
3
6
7
8
9
nil
Другой вариант - использовать agents, в этом случае вы бы send
или send-off
, и он будет использовать Thread из бассейна.
user=> (def a (agent 0))
#'user/a
user=> (dotimes [_ 10] (send a inc))
nil
;; ...later...
user=> @a
10
Еще один вариант: pcalls
и pmap
. Там также future
. Все они описаны в Clojure API.
Ответ 2
Обычно, когда я хочу начать поток в Clojure, я просто использую future.
Помимо простого использования, это имеет то преимущество, что вам не нужно делать какой-либо беспорядочный Java-взаимодействие для доступа к базовым механизмам потоков Java.
Использование примера:
(future (some-long-running-function))
Это приведет к асинхронному выполнению функции в другом потоке.
(def a (future (* 10 10)))
Если вы хотите получить результат, просто разыщите будущее, например:
@a
=> 100
Обратите внимание, что @a будет блокироваться, пока будущий поток не завершит свою работу.
Ответ 3
Программирование Clojure не затрагивает этот вопрос до тех пор, пока страница 167: "Использовать агенты для асинхронных обновлений".
Перед тем, как начинать темы, обратите внимание, что Clojure будет многозадачно, учитывая половину шанса. Я написал программы, беспечно не знающие concurrency, и обнаружил, что, когда условия правильные, они занимают более одного процессора. Я знаю, что это не очень строгое определение: я еще не исследовал это подробно.
Но для тех случаев, когда вам действительно нужна отдельная отдельная деятельность, один из ответов Clojure, по-видимому, является агентом.
(agent initial-state)
создаст его. Это не похоже на Java-поток, поскольку он является блоком кода, ожидающим исполнения. Вместо этого это действие, ожидающее выполнения работы. Вы делаете это через
(send agent update-fn & args)
Пример
(def counter (agent 0))
counter
- ваше имя и дескриптор для агента; состояние агента - это число 0.
Установив это, вы можете отправить работу агенту:
(send counter inc)
скажет, чтобы применить данную функцию к ее состоянию.
Вы можете позже вывести состояние из агента, разыгрывая его:
@counter
даст вам текущее значение числа, которое начиналось с 0.
Функция await
позволит вам сделать что-то вроде join
для активности агента, если она длинная:
(await & agents)
будет ждать, пока они все закончится; там также другая версия, которая занимает тайм-аут.
Ответ 4
Да, способ, которым вы запускаете Java-поток в Clojure, похож на то, что у вас есть.
Однако, реальный вопрос: зачем вам это делать? Clojure имеет гораздо лучшие конструкторы concurrency, чем потоки.
Если вы посмотрите на канонический пример concurrency в Clojure, Rich Hickey ant colony simulation, вы увидите, что использует ровно 0 потоков. Единственная ссылка на java.lang.Thread
во всем источнике - это три вызова Thread.sleep
, единственная цель которых - замедлить симуляцию вниз, чтобы вы действительно могли видеть, что происходит в пользовательском интерфейсе.
Вся логика выполняется в Агенты: один агент для каждого ant, один агент для анимации и один агент для испарения феромонов. Игровое поле является транзакционным реф. Не поток, ни замок в поле зрения.
Ответ 5
Использование будущего - это, как правило, самый простой доступ к потоковому доступу. В зависимости от того, что вы хотите сделать:)
Ответ 6
Просто чтобы добавить мои два цента (через 7 лет): Clojure функции реализуют IFn
интерфейс, который расширяет Callable
как а также Runnable
. Следовательно, вы можете просто передать их таким классам, как Thread
.
Если ваш проект может уже использовать core.async, я предпочитаю использовать макрос go
:
(go func)
Выполняет func
в суперлегком поток IOC (инверсия управления):
go [...] превратит тело в конечный автомат. По достижении какой-либо операции блокировки конечный автомат будет "припаркован", и фактический поток управления будет освобожден. [...] Когда операция блокировки завершена, код будет возобновлен [...]
В случае, если func
собирается выполнять I/O или какую-то длительную задачу, вы должны использовать Thread
, который также является частью core.async(проверьте этот отличный пост в блоге):
(thread func)
В любом случае, если вы хотите придерживаться синтаксиса Java interop, рассмотрите макрос ->
(поток/стрелка):
(-> (Thread. func) .start)
Ответ 7
Маска (future f)
обертывает форму f в Callable (через fn *) и немедленно отправляет ее в пул потоков.
если вам нужна ссылка на объект java.lang.Thread, например, чтобы использовать его как java.lang.Runtime shutdown hook, вы можете создать Thread следующим образом:
(proxy [Thread] [] (run [] (println "running")))
Это еще не запустит поток, создайте его. Чтобы создать и запустить поток, отправьте его в пул потоков или вызовите .start на нем:
(->
(proxy [Thread] [] (run [] (println "running")))
(.start))
Ответы Brians также создают поток, но не нуждаются в прокси, так что очень элегантно. С другой стороны, используя прокси-сервер, мы можем избежать создания Callable.