Вопрос

Я пытаюсь изучить Clojure, используя API и документацию, доступную на сайте.Мне немного неясно, что такое изменяемое хранилище в Clojure, и я хочу убедиться, что мое понимание правильное.Пожалуйста, дайте мне знать, если есть какие-либо идеи, в которых я ошибся.

Редактировать:Я обновляю это по мере получения комментариев о его правильности.


Отказ от ответственности:Вся эта информация неофициальна и потенциально неверна.Не используйте этот пост для понимания того, как работает Clojure.


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

Ссылки — это местоположения, совместно используемые потоками, которые поддерживают атомарные транзакции, которые могут изменить состояние любого количества ссылок в одной транзакции.Транзакции фиксируются при выходе из выражений синхронизации (dosync), а конфликты разрешаются автоматически с помощью магии STM (откаты, очереди, ожидания и т. д.).

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

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

Вот мое краткое изложение, основанное на том, когда использовать эти структуры:

  • Вары похожи на обычные старые переменные в императивных языках.(избегайте, когда это возможно)
  • Атомы похожи на Vars, но с безопасностью совместного использования потоков, которая обеспечивает немедленное чтение и безопасную настройку. (спасибо Мартин)
  • Агент похож на атом, но вместо того, чтобы блокировать его, он создает новый поток для вычисления его значения, блокируется только в середине изменения значения и может сообщить другим потокам, что он завершил присвоение.
  • Ссылки — это общие местоположения, которые блокируются в транзакциях.Вместо того, чтобы заставлять программиста решать, что происходит во время гонок для каждого фрагмента заблокированного кода, мы просто запускаем транзакцию и позволяем Clojure обрабатывать все условия блокировки между ссылками в этой транзакции.

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

Это было полезно?

Решение

Похоже, вы действительно получаете Clojure!хорошая работа :)

У переменных есть «корневая привязка», видимая во всех потоках, и каждый отдельный поток может изменить значение, которое он видит, не затрагивая другие потоки.Если я правильно понимаю, переменная не может существовать только в одном потоке без корневой привязки, видимой для всех, и не может быть «восстановлена», пока не будет определена с помощью (def ...) первый раз.

Ссылки фиксируются в конце (dosync...) транзакция, которая включает в себя изменения, но только тогда, когда транзакция смогла завершиться в согласованном состоянии.

Другие советы

Я думаю, что ваш вывод об атомах неверен:

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

Атомы изменяются с swap! или низкого уровня с compare-and-set!.Это никогда ничего не блокирует. swap! работает как транзакция только с одной ссылкой:

  1. старое значение берется из атома и сохраняется локально в потоке
  2. функция применяется к старому значению для создания нового значения
  3. если это удается, вызывается функция сравнения и установки со старым и новым значением;только если значение атома не было изменено каким-либо другим потоком (все еще равно старому значению), записывается новое значение, в противном случае операция перезапускается с (1) до тех пор, пока в конечном итоге не завершится успешно.

Я обнаружил две проблемы в вашем вопросе.

Ты говоришь:

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

http://clojure.org/agents говорит:

состояние Агента всегда доступно для чтения любому потоку.

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

Код для deref-метод Agent выглядит так (версия SVN 1382):

public Object deref() throws Exception{
    if(errors != null)
    {
        throw new Exception("Agent has errors", (Exception) RT.first(errors));
    }
return state;

}

Никакой блокировки не происходит.

Кроме того, я не понимаю, что вы имеете в виду (в разделе «Ссылка») под

Транзакции фиксируются при вызове deref

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

Мартин прав, когда говорит, что работа Atoms возобновляется с 1.до тех пор, пока в конечном итоге это не удастся.Это также называется ожиданием вращения.Хотя на самом деле блокируется блокировка, поток, выполнивший операцию, блокируется до тех пор, пока операция не завершится успешно, поэтому это блокирующая операция, а не асинхронная операция.

Что касается фьючерсов, в Clojure 1.1 добавлены абстракции для обещаний и фьючерсов.Промис — это конструкция синхронизации, которую можно использовать для доставки значения из одного потока в другой.Пока значение не будет доставлено, любая попытка разыменовать обещание будет заблокирована.

(def a-promise (promise))
(deliver a-promise :fred)

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

(def f (future (some-sexp)))
(deref f) ; blocks the thread that derefs f until value is available

Вары не всегда имеют корневую привязку.Разрешено создавать переменную без привязки, используя

(def x)

или

(declare x)

Попытка вычислить x до того, как он примет значение, приведет к

Var user/x is unbound.
[Thrown class java.lang.IllegalStateException]
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top