Чем отличаются фьючерсы и обещания Clojure?
И фьючерсы, и обещания блокируются, пока они не вычислили свои значения, так в чем же разница между ними?
6 ответов
Отвечая в терминах Clojure, вот несколько примеров из скриншота Шона Девлина:
(def a-promise (promise))
(deliver a-promise :fred)
(def f (future (some-sexp)))
(deref f)
Обратите внимание, что в обещании вы явно предоставляете значение, которое вы выбрали в последующих вычислениях (:fred
в этом случае). С другой стороны, будущее потребляется там же, где оно было создано. some-expr
предположительно запускается за кулисами и рассчитывается в тандеме (в конце концов), но если он остается неоцененным к моменту обращения к блокам потоков, пока не станет доступным.
отредактировано, чтобы добавить
Чтобы помочь в дальнейшем отличить обещание от будущего, обратите внимание на следующее:
обещание
- Вы создаете
promise
, Этот объект обещания теперь может быть передан в любой поток. - Вы продолжаете вычисления. Это могут быть очень сложные вычисления, включающие побочные эффекты, загрузку данных, ввод данных пользователем, доступ к базе данных, другие обещания - что угодно. Код будет очень похож на ваш основной код в любой программе.
- Когда вы закончите, вы можете
deliver
результаты этого обещанного объекта. - Любой предмет, который пытается
deref
Ваше обещание, прежде чем вы закончите с вашими расчетами, будет заблокировано, пока вы не закончите Как только вы закончите, и выdeliver
обещание, обещание больше не будет блокировать.
будущее
- Вы создаете свое будущее. Частью вашего будущего является выражение для расчета.
- Будущее может или не может быть выполнено одновременно. Может быть назначен поток, возможно, из пула. Это может просто подождать и ничего не делать. С вашей точки зрения вы не можете сказать.
- В какой-то момент вы (или другой поток)
deref
будущее. Если расчет уже завершен, вы получите его результаты. Если он еще не завершен, вы блокируете, пока не завершите. (Предположительно, если это еще не началось,deref
Это означает, что он начинает выполняться, но это тоже не гарантируется.)
Хотя вы можете сделать выражение в будущем таким же сложным, как и код, который следует за созданием обещания, сомнительно, что это желательно. Это означает, что фьючерсы действительно больше подходят для быстрых фоновых вычислений, в то время как обещания действительно больше подходят для больших и сложных путей выполнения. Кроме того, обещания кажутся, с точки зрения доступных расчетов, немного более гибкими и ориентированными на создателя обещаний, выполняющего работу, и на другой поток, собирающий урожай. Фьючерсы больше ориентированы на автоматический запуск потока (без уродливых и подверженных ошибкам накладных расходов) и на другие вещи, пока вам - исходному потоку - не понадобятся результаты.
И Future, и Promise являются механизмами передачи результатов асинхронных вычислений от производителя к потребителю (-ям).
В случае Future вычисление определяется во время создания Future, и асинхронное выполнение начинается как можно скорее. Он также "знает", как создавать асинхронные вычисления.
В случае Promise вычисление, его время начала и [возможный] асинхронный вызов отделяются от механизма доставки. Когда результат вычисления доступен, производитель должен вызвать deliver
явно, что также означает, что Producer контролирует, когда результат становится доступным.
Для обещаний Clojure допускает ошибку проектирования, используя тот же объект (результат promise
позвони) чтобы оба производили (deliver
) и потреблять (deref
) результат вычисления. Это две совершенно разные возможности и должны рассматриваться как таковые.
Уже есть отличные ответы, поэтому добавим только резюме "как использовать":
И то и другое
Создание обещания или будущего немедленно возвращает ссылку. Эта ссылка блокируется на @/deref до тех пор, пока результат вычисления не будет предоставлен другим потоком.
Будущее
При создании будущего вы предоставляете синхронную работу. Он выполняется в потоке из выделенного неограниченного пула.
обещание
Вы не приводите аргументов при создании обещания. Ссылка должна быть передана другому потоку пользователя, который будет deliver
результат.
В Clojure promise
, future
, а также delay
объекты, похожие на обещания. Все они представляют собой вычисления, которые клиенты могут ожидать, используяderef
(или @
). Клиенты повторно используют результат, поэтому вычисление не запускается несколько раз.
Они различаются способом выполнения вычислений:
future
начнет вычисление в другом рабочем потоке.deref
будет блокировать, пока не будет готов результат.delay
будет выполнять вычисление лениво, когда первый клиент используетderef
, илиforce
.promise
предлагает максимальную гибкость, так как его результат доставляется любым индивидуальным способом с помощьюdeliver
. Вы используете его, когда ниfuture
илиdelay
соответствовать вашему варианту использования.
Я думаю, что глава 9 Clojure for the Brave лучше всего объясняет разницу между
Идея, объединяющая эти три концепции, заключается в следующем: жизненный цикл задачи. Задачу можно представить как прохождение трех этапов: задача определяется, задача выполняется, результат задачи используется.
Некоторые языки программирования (например, JavaScript) имеют конструкции с одинаковыми названиями (например, JS), которые объединяют несколько (или все) этапов жизненного цикла задачи. В JS, например, невозможно построить объект, не предоставив ему либо функцию (задачу), которая будет вычислять его значение, либо
Clojure, однако, избегает такой связи и по этой причине имеет три отдельные конструкции, каждая из которых соответствует одному этапу жизненного цикла задачи.
-
определение задачи -
: выполнение задачи - : результат задачи
Каждая конструкция связана со своим собственным этапом жизненного цикла задачи и ни с чем другим, таким образом распутывая конструкции более высокого порядка, такие как JS, и разделяя их на соответствующие части.
Теперь мы видим, что в JavaScript a - это комбинация всех трех конструкций Clojure, перечисленных выше. Пример:
const promise = new Promise((resolve) => resolve(6))
Давайте разберемся:
- определение задачи:
это задача. - выполнение задачи: здесь подразумевается контекст выполнения, а именно то, что эта задача будет выполняться в будущем цикле цикла событий. Вы не имеете права голоса в этом; вы не можете, например, требовать, чтобы эта задача решалась синхронно, потому что асинхронность встроена сама в себя. Обратите внимание, как при построении вы уже запланировали выполнение своей задачи (в какое-то неопределенное время). Вы не можете сказать «позвольте мне передать это другому компоненту моей системы и позволить ему решать, когда он хочет запустить эту задачу».
- результат задачи: результат задачи запекается в
объект и может быть получен ing или ing. Невозможно создать «пустой» обещанный результат, который позже будет заполнен какой-то еще неизвестной частью вашей системы; вы должны как определить задачу, так и одновременно запланировать ее выполнение.
PS: Разделение, которое накладывает Clojure, позволяет этим конструкциям брать на себя роли, для которых они не подходили бы, если бы были тесно связаны. Например, Clojure
Во-первых, Promise
это Future
, Я думаю, что вы хотите знать разницу между Promise
и FutureTask
,
Future
представляет значение, которое в настоящее время не известно, но будет известно в будущем.
FutureTask
представляет результат вычисления, которое произойдет в будущем (возможно, в каком-то пуле потоков). Когда вы пытаетесь получить доступ к результату, если вычисление еще не произошло, оно блокируется. В противном случае результат возвращается немедленно. Нет другой стороны, участвующей в вычислении результата, так как вычисление указано вами заранее.
Promise
представляет результат, который будет доставлен обещателем в будущем. В этом случае вы обещаете, а тот, кто дал вам Promise
объект. Похож на FutureTask
, если вы пытаетесь получить доступ к результату до Promise
выполнено, блокируется до тех пор, пока Promise
, Однажды Promise
выполняется, вы получаете одно и то же значение всегда и сразу. В отличие от FutureTask
есть еще одна сторона, которая сделала Promise
, Что другая сторона несет ответственность за выполнение вычислений и выполнение Promise
,
В этом смысле FutureTask
это Promise
ты сделал для себя.