2009-06-22 2 views
26

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

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


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


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

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

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

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

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

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

Также, связанная с этим концепция - это функция future. Для меня кажется, что будущий объект можно охарактеризовать как синхронный Агент, где невозможно получить доступ к значению до тех пор, пока вычисление не будет завершено. Он также может быть описан как неблокирующий Atom. Являются ли эти точные концепции будущего?

ответ

5

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

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

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

+0

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

3

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

Вы говорите:

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

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

состояние Агента всегда доступно для считывания любой нитью

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

Код для 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 не имеет к этому никакого отношения, но, возможно, я неправильно понимаю вашу точку зрения.

+0

Hi pmf, я чувствую, что вы смущены об Агенты. Состояние * агента * всегда доступно, но его значение изменяется асинхронно Clojure, когда функция действия завершается после вызова send. В своем объяснении я имел в виду только последний шаг отправки агента: «5. Если во время выполнения функции будут сделаны какие-либо другие рассылки (прямо или косвенно), они будут удерживаться до тех пор, пока состояние Агента не будет изменилось «. Это единственная блокировка, происходящая здесь. – Kai

+0

Мое заявление о совершении транзакций происходит из памяти на основе слайдов, которые я читал у Rich Hickley, хотя я, вероятно, ошибаюсь. Для меня также имеет смысл совершить после dosync. – Kai

4

Я думаю, что ваш вывод о Атомы неправильно:

Атомы как Варс, но с безопасностью потоков обмена, который блокирует, пока значение не изменилось

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

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

А, я думаю, вы правы, нет блокировки. В этом случае мне интересно, что это значит, когда говорят, что атомы синхронны. – Kai

+0

свопы не совсем похожи на транзакции, так как они не связаны с другими транзакциями - будьте осторожны, если вы используете swap! в рамках транзакции, поскольку она не является безопасной транзакцией, в частности, она может несколько раз повториться или вмешиваться в другую транзакцию. – mikera

1

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

Также о Futures, 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 
0

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

(def x) 

или

(declare x) 

Попытка оценить х, прежде чем он имеет значение приведет к

Var user/x is unbound. 
[Thrown class java.lang.IllegalStateException] 
Смежные вопросы