Различия в Clojure между Ref, Var, Agent, Atom с примерами

110

Я новичок в Clojure, можете ли вы, ребята, дать мне объяснение с помощью реальных сценариев. То есть где использовать Ref, Var, Agent, Atom. Я читал книгу, но все еще не мог понять реальных примеров.

Майк
источник

Ответы:

174

Я настоятельно рекомендую "The Joy of Clojure" или "Программирование Clojure" для реального ответа на этот вопрос, я могу воспроизвести короткий фрагмент мотивации для каждого из них:

начните с просмотра этого видео о понятии идентичности и / или изучите здесь .

  • Ссылки предназначены для координированного синхронного доступа к «многим идентификаторам».
  • Атомы предназначены для нескоординированного синхронного доступа к одному удостоверению.
  • Агенты предназначены для нескоординированного асинхронного доступа к одной личности.
  • Переменные предназначены для локальных изолированных идентификаторов потока с общим значением по умолчанию.

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

Некоординированный доступ используется, когда требуется обновить только одно удостоверение, это очень распространенный случай.

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

Асинхронный доступ - это «выстрелил и забыл», и позволить Identity достичь своего нового состояния в свое время.

Артур Ульфельдт
источник
При координированном доступе, если я хочу только изменить state-a, но при этом ссылаюсь state-b, мне все равно нужен refправильный? Значит, это не изменение нескольких вещей, а ссылка на несколько вещей при изменении любого из них?
event_jr
2
Да, вы, кажется, правильно понимаете, что и состояние-a, и состояние-b должны быть ссылками. Если вы хотите, чтобы новое значение в состоянии-a основывалось на последовательной комбинации значений в a и b. Вам нужно, чтобы это новое значение было вычислено в контексте, где состояние-a и состояние-b согласованы друг с другом. Когда они оба являются ссылками, если b изменяется в середине, то транзакция перезапускается и использует новые значения как a, так и b. рассмотрите возможность использования ensureфункции: clojure.github.io/clojure/clojure.core-api.html#clojure.core/…, чтобы сделать это явным и более эффективным.
Артур Ульфельдт
3
Может быть, для завершения ответа можно добавить объяснение того, что означает «Изолированный с общими средствами по умолчанию»?
Дидье А.
1
«Скоординированный доступ используется, когда необходимо изменить два удостоверения вместе ...». Следует ли это «изменить»?
Carcigenicate
40

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

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

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

Переменные используются, когда вам нужно хранить что-то для каждого потока. Если у вас многопоточная программа, и каждому потоку требуется собственное частное состояние, поместите это состояние в переменную.

Что касается реальных примеров, то если вы приведете пример того, что пытаетесь сделать, мы можем сказать вам, что использовать.

Retief
источник
32

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

Используйте переменную, если данные не изменяются. Это происходит всякий раз, когда вы используете defили большинство функций, которые начинаются с deflike defn.

Используйте атом, если у вас есть один изменяемый элемент. Примером может быть счетчик или вектор, в который вы хотите добавить элементы.

Используйте реф, если у вас есть две или более вещи, которые должны измениться одновременно. Подумайте о «транзакциях базы данных», если вы знакомы. Канонический пример этого - перевод денег с одного счета на другой. Каждую учетную запись можно сохранить в ссылке, чтобы изменения могли быть атомарными.

Используйте агента, когда хотите что-то изменить, но вам все равно, когда. Это может быть долгое вычисление или запись чего-либо в файл или сокет. Обратите внимание, что с последним вы должны использовать send-off.

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

Optevo
источник
1
Большое спасибо за четкий ответ :-) Очень помогает новичку в Clojure вроде меня.
gosukiwi
27

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

Поделиться состоянием - когда используются вары, атомы, агенты и ссылки?

Надеюсь, это поможет людям, ищущим ответы в этой теме.

Некоторый ярлык из статьи после предложения @tunaci:

Варс

Варки глобальны для каждого потока.

Не изменяйте вары после создания. Это технически возможно, но по многим причинам это плохая идея.

Атомы

Делитесь доступом к изменяемому состоянию для каждого потока. Смена происходит синхронно. Повторите попытку, когда другой поток изменит состояние во время выполнения.

Не используйте неидемпотентные функции и функции с длительным исполнением

Агенты

Делитесь доступом к изменяемому состоянию для каждого потока. Изменение происходит асинхронно.

Refs

Refs работает аналогично транзакциям с базой данных. Запись и чтение защищены в dosync. Вы можете работать со многими рефери, безопасно в сделке.

И блок-схема при использовании какой из них: блок-схема

Пожалуйста, посмотрите изображение на сайте, потому что обновления возможны всегда.

Это сложная и длинная тема, чтобы дать полный ответ без копирования и прошлой статьи, поэтому, пожалуйста, простите меня, я перенаправляю вас на сайт :)

кабра
источник