Фьючерсы и обещания

редактировать
Не путать с теорией обещаний.

В информатике, future, prom, delay и deferred относятся к конструкциям, используемым для синхронизации выполнения программы в некоторых языках параллельного программирования. Они описывают объект, который действует как прокси для результата, который изначально неизвестен, обычно потому, что вычисление его значения еще не завершено.

Термин « обещание» был предложен в 1976 году Дэниелом П. Фридманом и Дэвидом Вайсом, а Питер Хиббард назвал его « возможным». Похожая концепция будущего была представлена ​​в 1977 году в статье Генри Бейкера и Карла Хьюитта.

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

СОДЕРЖАНИЕ

  • 1 Приложения
  • 2 Неявное против явного
  • 3 конвейерная обработка обещаний
  • 4 режима просмотра только для чтения
  • 5 Зависящие от потока фьючерсы
  • 6 Блокирующая и неблокирующая семантика
  • 7 Родственные конструкции
  • 8 Отношения между выразительностью разных форм будущего
  • 9 Стратегия оценки
  • 10 Семантика фьючерсов в актерской модели
  • 11 История
  • 12 Список реализаций
    • 12.1 Список понятий, относящихся к Futures и Promises в языке программирования
    • 12.2 Список нестандартных библиотечных реализаций фьючерсов
    • 12.3 Сопрограммы
    • 12.4 Каналы
  • 13 См. Также
  • 14 Ссылки
  • 15 Внешние ссылки

Приложения

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

Неявное и явное

Использование фьючерсов может быть неявным (любое использование будущего автоматически получает его значение, как если бы это была обычная ссылка ) или явным (пользователь должен вызвать функцию для получения значения, например, getметод java.util.concurrent.Future в Java ). Получение ценности явного будущего можно назвать язвой или принуждением. Явные фьючерсы могут быть реализованы как библиотека, тогда как неявные фьючерсы обычно реализуются как часть языка.

В исходной статье Бейкера и Хьюитта описывались неявные варианты будущего, которые естественным образом поддерживаются в акторной модели вычислений и чистых объектно-ориентированных языках программирования, таких как Smalltalk. В статье Фридмана и Уайза описываются только явные фьючерсы, что, вероятно, отражает сложность эффективной реализации неявных фьючерсов на стандартном оборудовании. Сложность в том, что стандартное оборудование не работает с фьючерсами для примитивных типов данных, таких как целые числа. Например, инструкция добавления не знает, что делать. В чистых языках акторов или объектов эта проблема может быть решена путем отправки сообщения, которое просит будущее добавить к себе и вернуть результат. Обратите внимание, что подход с передачей сообщений работает независимо от того, когда завершаются вычисления, и что никаких ограничений / принуждения не требуется. 3 + future factorial(100000)future factorial(100000)+[3]3factorial(100000)

Обещание конвейерной обработки

Использование фьючерсов может значительно сократить время ожидания в распределенных системах. Например, фьючерсы включают конвейерную обработку обещаний, как это реализовано в языках E и Joule, что также называлось потоком вызовов на языке Argus.

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

 t3 := ( x.a()).c( y.b())

который может быть расширен до

 t1 := x.a(); t2 := y.b(); t3 := t1.c(t2);

Для каждого оператора необходимо отправить сообщение и получить ответ, прежде чем можно будет продолжить выполнение следующего оператора. Предположим, например, что x, y, t1и t2все они расположены на одной и той же удаленной машине. В этом случае должны произойти два полных сетевых обхода к этой машине, прежде чем может начаться выполнение третьего оператора. Третий оператор затем вызовет еще одно обращение к той же удаленной машине.

Используя фьючерсы, приведенное выше выражение можно было бы записать

 t3 := (x lt;- a()) lt;- c(y lt;- b())

который может быть расширен до

 t1 := x lt;- a(); t2 := y lt;- b(); t3 := t1 lt;- c(t2);

Здесь используется синтаксис языка E, где x lt;- a()означает a()асинхронную отправку сообщения x. Всем трем переменным немедленно назначаются фьючерсы на их результаты, и выполнение переходит к последующим операторам. Последующие попытки разрешить значение t3могут вызвать задержку; однако конвейерная обработка может сократить количество необходимых циклов передачи данных. Если же, как и в предыдущем примере, x, y, t1, и t2все они расположены на одной и той же удаленной машине, Конвейерная реализация может вычислить t3с одной редиректа вместо трех. Поскольку все три сообщения предназначены для объектов, находящихся на одной и той же удаленной машине, необходимо отправить только один запрос и получить только один ответ, содержащий результат. Посыла t1 lt;- c(t2)не будет блокировать, даже если t1и t2были на разных машинах друг с другом, или, xили y.

Конвейеризацию обещаний следует отличать от параллельной асинхронной передачи сообщений. В системе, поддерживающой параллельные передачи сообщений, но не конвейер, сообщение посылает x lt;- a()и y lt;- b()в приведенном выше примере может происходить параллельно, но посылу t1 lt;- c(t2)придется подождать, пока оба t1и t2было получено, даже когда x, y, t1, и t2находятся на то же отдаленное машина. Преимущество конвейерной обработки в относительной задержке становится еще больше в более сложных ситуациях, связанных с большим количеством сообщений.

Конвейерная обработка обещаний также не следует путать с конвейерной обработкой сообщений в системах субъектов, где субъект может указать и начать выполнение поведения для следующего сообщения до завершения обработки текущего сообщения.

Представления только для чтения

В некоторых языках программирования, таких как Oz, E и AmbientTalk, можно получить представление будущего только для чтения, которое позволяет читать его значение при разрешении, но не позволяет его разрешить:

  • В Oz !!оператор используется для получения представления только для чтения.
  • В E и AmbientTalk будущее представлено парой значений, называемой парой обещание / преобразователь. Обещание представляет собой представление, доступное только для чтения, и преобразователь необходим для установки будущего значения.
  • В C ++ 11 a std::futureпредоставляет представление только для чтения. Значение устанавливается напрямую с помощью a std::promiseили устанавливается равным результату вызова функции с помощью std::packaged_taskили std::async.
  • В Deferred API Dojo Toolkit версии 1.5 объект обещания только для потребителя представляет собой представление, доступное только для чтения.
  • В Alice ML фьючерсы предоставляют представление только для чтения, тогда как обещание содержит как будущее, так и возможность разрешить будущее.
  • В .NET Framework 4.0 System.Threading.Tasks.Tasklt;Tgt; представляет собой представление только для чтения. Разрешить значение можно с помощью System.Threading.Tasks.TaskCompletionSourcelt;Tgt;.

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

Зависящие от потока фьючерсы

Некоторые языки, такие как Alice ML, определяют фьючерсы, связанные с конкретным потоком, который вычисляет будущее значение. Это вычисление может начинаться либо нетерпеливо, когда создается будущее, либо лениво, когда его значение впервые требуется. Ленивое будущее похоже на преобразователь в смысле отложенных вычислений.

Алиса ML также поддерживает фьючерсы, которые могут быть решены любым потоком, и называет эти обещания. Такое использование обещания отличается от его использования в E, как описано выше. В Алисе промис - это не доступное только для чтения представление, и конвейерная обработка промисов не поддерживается. Вместо этого конвейерная обработка, естественно, происходит для фьючерсов, в том числе связанных с обещаниями.

Блокирующая и неблокирующая семантика

Если к значению будущего обращаются асинхронно, например, отправляя ему сообщение или явно ожидая его с помощью конструкции, такой как whenв E, то нетрудно отложить, пока будущее не будет разрешено, прежде чем сообщение может быть получено или ожидание завершается. Это единственный случай, который следует рассматривать в чисто асинхронных системах, таких как чистые языки акторов.

Однако в некоторых системах также может быть возможно попытаться немедленно или синхронно получить доступ к будущему значению. Затем следует сделать выбор дизайна:

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

В качестве примера первой возможности в C ++ 11 поток, которому требуется значение future, может блокироваться, пока оно не станет доступным, путем вызова функций-членов wait()или get(). Вы также можете указать тайм-аут ожидания с помощью функций-членов wait_for()или, wait_until()чтобы избежать неопределенной блокировки. Если будущее возникло из вызова, std::asyncто блокирующее ожидание (без тайм-аута) может вызвать синхронный вызов функции для вычисления результата в ожидающем потоке.

Связанные конструкции

Future - это частный случай Event (примитива синхронизации), который может быть выполнен только один раз. В общем, события могут быть сброшены в исходное пустое состояние и, таким образом, завершены сколько угодно раз.

I-вар (как в языке Id ) представляет собой будущее с блокировкой семантику, как определено выше. I-структура представляет собой структуру данных, содержащую I-вары. Связанная конструкция синхронизации, которая может быть установлена ​​несколько раз с разными значениями, называется M-var. M-vars поддерживают атомарные операции для получения или помещения текущего значения, где принятие значения также возвращает M-var обратно в исходное пустое состояние.

Одновременно логические переменная похожа на будущее, но обновляются объединениями, так же, как логические переменные в логическом программировании. Таким образом, его можно связать более одного раза с унифицируемыми значениями, но нельзя вернуть в пустое или неразрешенное состояние. Переменные потока данных Oz действуют как параллельные логические переменные, а также имеют семантику блокировки, как упомянуто выше.

Одновременно с переменным ограничением является обобщение параллельного логической переменных для поддержки ограничений логического программирования : ограничение может быть сужены несколько раз, что указует на меньшие наборов возможных значений. Обычно существует способ указать преобразователь, который должен запускаться всякий раз, когда ограничение еще больше сужается; это необходимо для поддержки распространения ограничений.

Отношения между выразительностью разных форм будущего

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

Для реализации неявных ленивых зависящих от потока фьючерсов (например, предоставленных Alice ML) в терминах фьючерсов, не зависящих от потока, необходим механизм для определения того, когда значение future необходимо в первую очередь (например, WaitNeededконструкция в Oz). Если все значения являются объектами, то возможности реализовать прозрачные объекты пересылки достаточно, поскольку первое сообщение, отправленное на сервер пересылки, указывает, что необходимо будущее значение.

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

Стратегия оценки

Дополнительная информация: Звонок будущим

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

А ленивое будущее - это будущее, которое детерминированно имеет семантику ленивых вычислений: вычисление будущего значения начинается, когда значение впервые требуется, как при вызове по необходимости. Ленивые фьючерсы используются в языках, в которых стратегия оценки по умолчанию не является ленивой. Например, в C ++ 11 такие ленивые фьючерсы можно создать, передавstd::launch::deferredполитику запускаstd::asyncвместе с функцией для вычисления значения.

Семантика фьючерсов в актерской модели

В модели актора выражение формы future lt;Expressiongt;определяется тем, как оно реагирует на Evalсообщение со средой E и клиентом C следующим образом: будущее выражение отвечает на Evalсообщение, отправляя клиенту C вновь созданного актора F (прокси для реакция оценки lt;Expressiongt;) в качестве возвращаемого значения одновременно с отправкой lt;Expressiongt;в Evalсообщении с окружающим Е и клиентом C. Поведение F по умолчанию выглядит следующим образом:

  • Когда F получает запрос R, он проверяет, получил ли он уже ответ (который может быть либо возвращаемым значением, либо вызванным исключением) от оценки, lt;Expressiongt;выполняя следующие действия:
    1. Если у него уже есть ответ V, то
      • Если V является возвращаемым значением, то он послал запрос R.
      • Если V является исключением, то она выбрасывается заказчику запрос R.
    2. Если он уже не имеет ответа, то R хранится в очереди запросов внутри F.
  • Когда F получает ответ V от оценки lt;Expressiongt;, тогда V сохраняется в F и
    • Если V является возвращаемым значением, то все очередей запросов направляются в V.
    • Если V является исключением, то оно передается заказчику каждого из запросов в очереди.

Однако некоторые фьючерсы могут обрабатывать запросы особым образом, чтобы обеспечить больший параллелизм. Например, выражение 1 + future factorial(n)может создать новое будущее, которое будет вести себя как число 1+factorial(n). Этот трюк не всегда срабатывает. Например, следующее условное выражение:

if mgt;future factorial(n) then print("bigger") else print("smaller")

приостанавливается до тех пор, пока future for factorial(n)не ответит на запрос, спрашивая, mбольше ли оно, чем оно.

История

Конструкции future и / или prom были впервые реализованы в таких языках программирования, как MultiLisp и Act 1. Использование логических переменных для связи в языках программирования параллельной логики было очень похоже на Futures. Они начались в Прологе с замораживанию и IC Прологом, и стали истинным параллелизмом примитива с реляционным языком, Concurrent Prolog, охранявший Хорн (GHC), Parlog, Strand, Vulcan, Янусом, Ог Моцартом, Flow Java, и Элис ML. I-var с одним присваиванием из языков программирования потоков данных, происходящий из Id и включенный в Concurrent ML Reppy, очень похож на переменную concurrent logic.

Техника конвейерной обработки обещаний (использование фьючерсов для преодоления задержки) была изобретена Барбарой Лисков и Любой Шрира в 1988 году и независимо друг от друга Марком Миллером, Дином Трибблом и Робом Джеллингхаусом в контексте проекта Xanadu примерно в 1989 году.

Термин обещание был придуман Лисковым и Шрирой, хотя они называли механизм конвейерной обработки именем call-stream, который сейчас используется редко.

И дизайн, описанный в статье Лискова и Шриры, и реализация конвейерной обработки обещаний в Xanadu, имели ограничение, согласно которому значения обещаний не были первоклассными : аргумент или значение, возвращаемое вызовом или отправкой, не могло напрямую быть обещанием (поэтому приведенный ранее пример конвейерной обработки обещаний, который использует обещание для результата одной отправки в качестве аргумента для другой, не мог быть напрямую выражен в дизайне потока вызовов или в реализации Xanadu). Похоже, что обещания и потоки вызовов никогда не были реализованы ни в одном публичном выпуске Argus, языка программирования, использованного в статье Лискова и Шриры. Разработка Argus остановилась примерно в 1988 году. Реализация конвейерной обработки обещаний в Xanadu стала общедоступной только с выпуском исходного кода для Udanax Gold в 1999 году и никогда не была объяснена ни в одном опубликованном документе. Более поздние реализации в Joule и E полностью поддерживают первоклассные обещания и преобразователи.

Несколько ранних языков акторов, включая серию Act, поддерживали как параллельную передачу сообщений, так и конвейерную обработку сообщений, но не обещали конвейерную обработку. (Хотя технически возможно реализовать последнюю из этих функций в первых двух, нет никаких доказательств того, что языки Act сделали это.)

После 2000 года произошло серьезное возрождение интереса к будущим и обещаниям из-за их использования для повышения отзывчивости пользовательских интерфейсов и в веб-разработке из -за модели передачи сообщений запрос-ответ. В некоторых основных языках теперь есть языковая поддержка для фьючерсов и обещаний, особенно популяризованных FutureTaskв Java 5 (анонсированной в 2004 г.) и конструкциях async / await в.NET 4.5 (анонсированной в 2010 г., выпущенной в 2012 г.), во многом вдохновленных асинхронными рабочими процессами F #, которые датируется 2007 годом. Впоследствии он был принят другими языками, особенно Dart (2014), Python (2015), Hack (HHVM) и проектами ECMAScript 7 (JavaScript), Scala и C ++.

Список реализаций

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

Список понятий, связанных с фьючерсами и обещаниями в языке программирования

Языки, также поддерживающие конвейерную обработку обещаний, включают:

Список нестандартных библиотечных реализаций фьючерсов

  • Для Common Lisp :
    • Черный дрозд
    • Нетерпеливое будущее2
    • lпараллельный
    • PCall
  • Для C ++:
  • Для C # и других языков .NET : библиотека параллельных расширений
  • Для Groovy : GPars
  • Для JavaScript :
    • Cujo.js 'when.js предоставляет обещания, соответствующие спецификации Promises / A + 1.1
    • Набор инструментов Dojo предоставляет обещания и отложенные объекты в стиле Twisted.
    • MochiKit, вдохновленный Deferreds от Twisted
    • Отложенный объект jQuery основан на дизайне CommonJS Promises / A.
    • AngularJS
    • узел -обещание
    • Q, созданный Крисом Ковалом, соответствует требованиям Promises / A + 1.1.
    • RSVP.js, соответствует Promises / A + 1.1
    • Класс обещаний YUI соответствует спецификации Promises / A + 1.0.
    • Синяя птица, Петька Антонов
    • Закрытие Библиотека «s обещание пакет соответствует к Обещания / A + спецификации.
    • См. Список Promise / A + для получения дополнительных реализаций, основанных на дизайне Promise / A +.
  • Для Java :
    • JDeferred, предоставляет API отложенного обещания и поведение, подобное объекту JQuery. Deferred
    • ParSeq предоставляет API-интерфейс обещания задач, идеально подходящий для асинхронной конвейерной обработки и ветвления, поддерживаемый LinkedIn.
  • Для Lua :
    • Модуль cqueues [1] содержит Promise API.
  • Для Objective-C : MAFuture, RXPromise, ObjC-CollapsingFutures, PromiseKit, objc-prom, OAPromise,
  • Для OCaml : модуль Lazy реализует ленивые явные фьючерсы
  • Для Perl : Future, Promises, Reflex, Promise:: ES6 и Promise:: XS
  • Для PHP : React / Promise
  • Для Python :
    • Встроенная реализация
    • pythonfutures
    • Отсрочка Twisted
  • Для R :
    • future, реализует расширяемый будущий API с ленивым и нетерпеливым синхронным и (многоядерным или распределенным) асинхронным будущим
  • Для Ruby :
    • Обещание драгоценного камня
    • libuv gem, реализует обещания
    • Целлулоидный драгоценный камень, реализует фьючерсы
    • будущий ресурс
  • Для Rust :
    • Futures-RS
  • Для Scala :
    • Библиотека утилит Twitter
  • Для Swift :
    • Async framework, реализует стиль C # async/ неблокирующийawait
    • FutureKit, реализует версию для Apple GCD
    • FutureLib, чистая библиотека Swift 2, реализующая фьючерсы и обещания в стиле Scala с отменой в стиле TPL
    • Отложенная, чистая библиотека Swift, вдохновленная OCaml Deferred
    • BrightFutures
    • SwiftCoroutine
  • Для Tcl : tcl-обещание

Сопрограммы

Фьючерсы могут быть реализованы в сопрограммах или генераторах, что приводит к той же стратегии оценки (например, совместная многозадачность или ленивая оценка).

каналы

Основная статья: Канал (программирование)

Фьючерсы могут быть легко реализованы в каналах : будущее - это одноэлементный канал, а обещание - это процесс, который отправляется в канал, выполняя будущее. Это позволяет реализовать фьючерсы на языках параллельного программирования с поддержкой каналов, таких как CSP и Go. Результирующие фьючерсы являются явными, поскольку доступ к ним должен осуществляться путем чтения из канала, а не только оценки.

Смотрите также

использованная литература

внешние ссылки

Последняя правка сделана 2023-03-31 11:56:52
Содержание доступно по лицензии CC BY-SA 3.0 (если не указано иное).
Обратная связь: support@alphapedia.ru
Соглашение
О проекте