Я пытаюсь изучить Clojure с помощью API и документации, доступных на сайте. Мне немного непонятно изменяемое хранилище в Clojure, и я хочу убедиться, что мое понимание правильное. Пожалуйста, дайте мне знать, если есть идеи, в которых я ошибся.
Изменить: я обновляю это, когда получаю комментарии о его правильности.
Отказ от ответственности: вся эта информация является неофициальной и потенциально неверной. Не используйте этот пост для понимания того, как работает Clojure.
Vars всегда содержат корневую привязку и, возможно, привязку для каждого потока. Они сопоставимы с обычными переменными в императивных языках и не подходят для обмена информацией между потоками. (спасибо Артуру Ульфельдту)
Refs — это местоположения, совместно используемые потоками, которые поддерживают атомарные транзакции, которые могут изменять состояние любого количества ссылок в одной транзакции. Транзакции фиксируются при выходе из выражений синхронизации (dosync), а конфликты разрешаются автоматически с помощью магии STM (откаты, очереди, ожидания и т. д.).
Агенты – это места, которые позволяют асинхронно обмениваться информацией между потоками с минимальными издержками, отправляя независимые функции действий для изменения состояния агента. Агенты возвращаются немедленно и поэтому не блокируются, хотя значение агента не устанавливается до тех пор, пока отправленная функция не завершится.
Атомы — это местоположения, которые могут быть синхронно разделены между потоками. Они поддерживают безопасные манипуляции между различными потоками.
Вот мое дружеское резюме, основанное на том, когда использовать эти структуры:
- Переменные похожи на обычные старые переменные в императивных языках. (избегайте, когда это возможно)
- Атомы похожи на Vars, но с безопасностью совместного использования потоков, что позволяет немедленно читать и безопасно устанавливать. (спасибо, Мартин)
- Агент подобен атому, но вместо того, чтобы блокировать, он порождает новый поток для вычисления его значения, блокируется только в середине изменения значения и может сообщить другим потокам, что он закончил назначение.
- Рефы — это общие местоположения, которые блокируют себя в транзакциях. Вместо того, чтобы заставлять программиста решать, что происходит во время условий гонки для каждого фрагмента заблокированного кода, мы просто запускаем транзакцию и позволяем Clojure обрабатывать все условия блокировки между ссылками в этой транзакции.
Кроме того, родственным понятием является функция future. Мне кажется, что будущий объект можно описать как синхронный агент, значение которого вообще невозможно получить, пока не будет завершено вычисление. Его также можно описать как неблокирующий атом. Являются ли это точными представлениями о будущем?