Как выполнить некоторые фьючерсы Clojure в одном потоке?

Я хотел бы создать некоторые фьючерсы в Clojure и запустить их все в определенном потоке, чтобы убедиться, что они запускают один за другим. Это возможно?

Это не сложно обернуть библиотеки Java, чтобы сделать это, но прежде чем я это сделаю, я хочу убедиться, что я не пропущу Clojure. В Java я могу это сделать, внедряя FutureTask и отправляя эти задачи однопоточному исполнителю.

0
Почему вы хотите создавать конструкции, предназначенные для параллелизма, только для обеспечения того, чтобы они выполнялись не одновременно? Возможно, у вас есть существующие задачи, определенные таким образом, или вы обычно выполняете их одновременно, но хотите обеспечить последовательное выполнение в течение ограниченного времени? Просто любопытно.
добавлено автор Josh, источник
Конечно, но зачем вообще создавать потоки? Почему бы просто не назвать задачи как функции из одного потока?
добавлено автор Josh, источник
Предположим, что существует несколько потоков (например, запросы потоков на сервере), все из которых хотят запускать задачи, которые используют общий ресурс X, который может использоваться только по одному потоку за раз. Одним из решений является захват блокировки при доступе к X. Другой способ заключается в отправке задач (фьючерсов) для запуска в одном потоке, связанном с X. Другие вызывающие потоки могут или не могут ждать результата в будущем.
добавлено автор Rob N, источник
Я думаю, что короткий ответ таков: потому что система выигрывает от параллельного выполнения в других областях. Как веб-сервер с запросами, обрабатываемыми отдельными потоками. Затем эти потоки должны делать вещи, которые используют ресурс X. Эта идея не является чем-то, что я придумал - это очередная очередь с асинхронной отправкой из GCD от Apple. Возможно, документы для этой системы объяснят это лучше. developer.apple.com/documentation/dispatch
добавлено автор Rob N, источник

8 ответы

Clojure будущий макрос вызывает future-call , которая использует выделенный сервис исполнителя . Это означает, что вы не можете контролировать последовательное выполнение.

С другой стороны, вы можете использовать обещание вместо будущих объектов и один future поток последовательно доставить результаты. API Promise аналогичен API future . У них deref и реализовано? .

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

(defn start-async-calc []
  (let [f1 (promise)
        f2 (promise)
        f3 (promise)]
    (future
      (deliver f1 (task-1))
      (deliver f2 (task-2))
      (deliver f3 (task-3)))
    {:task1 f1
     :task2 f2
     :task3 f3}))
2
добавлено

Clojure будущий макрос вызывает future-call , которая использует выделенный сервис исполнителя . Это означает, что вы не можете контролировать последовательное выполнение.

С другой стороны, вы можете использовать обещание вместо будущих объектов и один future поток последовательно доставить результаты. API Promise аналогичен API future . У них deref и реализовано? .

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

(defn start-async-calc []
  (let [f1 (promise)
        f2 (promise)
        f3 (promise)]
    (future
      (deliver f1 (task-1))
      (deliver f2 (task-2))
      (deliver f3 (task-3)))
    {:task1 f1
     :task2 f2
     :task3 f3}))
2
добавлено

если вы хотите секвенировать вызовы на future , вы можете использовать его вручную следующим образом:

(do @(future 1)
    @(future 2)
    @(future 3))

они все равно могли бы быть вызваны в разных потоках, но следующий не будет вызываться до тех пор, пока предыдущий не завершится. Это гарантируется функцией @ (или deref ). Это означает, что поток, в котором вы выполняете форму do , будет заблокирован предыдущим предложением перед его завершением, а затем появится следующий.

вы можете прикрыть его макросом следующим образом:

(defmacro sequentialize [& futures]
  `(do [email protected](map #(list `deref %) futures)))

user> (let [a (atom 1)]
        (sequentialize
         (future (swap! a #(* 10 %)))
         (future (swap! a #(+ 20 %)))
         (future (swap! a #(- %))))
        @a)
;;=> -30

это делает то же самое, что и ручной do . Обратите внимание, что мутации в a находятся в порядке, даже если некоторые потоки работают дольше:

user> (let [a (atom 1)]
        (sequentialize
         (future (Thread/sleep 100)
                 (swap! a #(* 10 %)))
         (future (Thread/sleep 200)
                 (swap! a #(+ 20 %)))
         (future (swap! a #(- %))))
        @a)
;;=> -30
1
добавлено

если вы хотите секвенировать вызовы на future , вы можете использовать его вручную следующим образом:

(do @(future 1)
    @(future 2)
    @(future 3))

они все равно могли бы быть вызваны в разных потоках, но следующий не будет вызываться до тех пор, пока предыдущий не завершится. Это гарантируется функцией @ (или deref ). Это означает, что поток, в котором вы выполняете форму do , будет заблокирован предыдущим предложением перед его завершением, а затем появится следующий.

вы можете прикрыть его макросом следующим образом:

(defmacro sequentialize [& futures]
  `(do [email protected](map #(list `deref %) futures)))

user> (let [a (atom 1)]
        (sequentialize
         (future (swap! a #(* 10 %)))
         (future (swap! a #(+ 20 %)))
         (future (swap! a #(- %))))
        @a)
;;=> -30

это делает то же самое, что и ручной do . Обратите внимание, что мутации в a находятся в порядке, даже если некоторые потоки работают дольше:

user> (let [a (atom 1)]
        (sequentialize
         (future (Thread/sleep 100)
                 (swap! a #(* 10 %)))
         (future (Thread/sleep 200)
                 (swap! a #(+ 20 %)))
         (future (swap! a #(- %))))
        @a)
;;=> -30
1
добавлено

Кроме того, упомянутый обещает , вы можете использовать delay . У обезьян есть проблема, что вы можете случайно их не доставить, и создать сценарий тупиковой ситуации, который невозможно с future s и delay s. Разница между будущим и задержкой - это только поток, на котором выполняется работа. В будущем работа выполняется в фоновом режиме, и с задержкой работа выполняется с помощью первого потока, который пытается его преодолеть. Поэтому, если будущее лучше, чем обещания, вы всегда можете сделать что-то вроде:

(def result-1 (delay (long-calculation-1)))
(def result-2 (delay (long-calculation-2)))
(def result-3 (delay (long-calculation-3)))

(defn run-calcs []
  @(future
     @result-1
     @result-2
     @result-3))
0
добавлено

Кроме того, упомянутый обещает , вы можете использовать delay . У обезьян есть проблема, что вы можете случайно их не доставить, и создать сценарий тупиковой ситуации, который невозможно с future s и delay s. Разница между будущим и задержкой - это только поток, на котором выполняется работа. В будущем работа выполняется в фоновом режиме, и с задержкой работа выполняется с помощью первого потока, который пытается его преодолеть. Поэтому, если будущее лучше, чем обещания, вы всегда можете сделать что-то вроде:

(def result-1 (delay (long-calculation-1)))
(def result-2 (delay (long-calculation-2)))
(def result-3 (delay (long-calculation-3)))

(defn run-calcs []
  @(future
     @result-1
     @result-2
     @result-3))
0
добавлено

Manifold provides a way to create future with specific executor. It's not part of core Clojure lib, but it's still a high quality lib and probably a best option in case you need more flexibility dealing with futures than core lib provides (without resorting to Java interop).

0
добавлено

Manifold provides a way to create future with specific executor. It's not part of core Clojure lib, but it's still a high quality lib and probably a best option in case you need more flexibility dealing with futures than core lib provides (without resorting to Java interop).

0
добавлено
pro.jvm
pro.jvm
3 503 участник(ов)

Сообщество разработчиков Java Scala Kotlin Groovy Clojure Чат для нач-их: @javastart Наш сайт: projvm.com projvm.ru Наш канал: @proJVM Вакансии: @jvmjobs Конфы: @jvmconf

Clojure — русскоговорящее сообщество
Clojure — русскоговорящее сообщество
433 участник(ов)

Общаемся на темы, посвященный Clojure. Решаем проблемы, обмениваемся опытом и делимся новостями. Вакансии и поиск работы: @clojure_jobs Вам могут быть интересны: @javascript_ru, @nodejs_ru, @ruby_ru, @devops_ru, @devops_jobs