Блок синхронизации Clojure STM (dosync) x Java

В чем разница между подходом Clojure STM (dosync) и блоком синхронизации Java?

Я читаю приведенный ниже код из проблемы «Спящий парикмахер». (http://www.bestinclass.dk/index.clj/2009/09/scala-vs-clojure-round-2-concurrency.html)

(defn the-shop [a]  
  (print "[k] entering shop" a)  
  (dosync     
    (if (< (count @queue) seats)  
      (alter queue conj a)  
      (print "[s] turning away customer" a))))

Чтобы избежать условий гонки, используется dosync, поэтому я спрашиваю себя: «В чем отличие (STM) от блока синхронизации Java»? Будет ли он блокировать этот критический код?

Заранее спасибо ! Дантас


person CHAPa    schedule 27.08.2010    source источник
comment
Привет, Михл, как будто ты отлично ответил мне о STM, позвольте мне задать еще один вопрос о clojure, который я не понимаю. Коммутационная форма. Предположим, что у меня есть 2 потока (t1 и t2). И идея заключается в увеличении ссылки, хорошо? t1 внутри dosync, получить значение ссылки - в значении транзакции (0) - ... t2 получить управление и смог увеличить ссылку с 0 до 1. t1 возвращается, и что он должен делать? если t1 продолжит выполнение, окончательный результат не будет правильным. как в этом случае работает коммутация? заранее спасибо   -  person CHAPa    schedule 14.11.2010


Ответы (4)


dosync и synchronized предоставляют доступ к совершенно разным абстракциям параллелизма.

synchronized — это способ получения и снятия блокировок. Когда поток входит в блок synchronized, он пытается получить соответствующую блокировку; если блокировка в настоящее время удерживается другим потоком, текущий поток блокируется и ожидает ее освобождения. Это приводит к определенным проблемам, таким как риск взаимоблокировки. Блокировка снимается, когда поток покидает блок synchronized.

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

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

Транзакция не рискует зайти в тупик (если только программист не приложит все усилия, чтобы ввести тупик, не связанный с системой STM, через взаимодействие Java); livelock, с другой стороны, является определенной возможностью, хотя и маловероятной. В общем, многие -- хотя и не все! -- интуиции, которые программисты связывают с транзакциями баз данных, действительны в контексте систем STM, включая Clojure.

STM — это огромная тема; одним из превосходных ресурсов для изучения STM Clojure является статья Марка Фолькманна Software Transactional Memory. В заключительных разделах мы подробно обсуждаем Clojure STM, но начало может послужить отличным вводным чтением.

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

person Michał Marczyk    schedule 27.08.2010
comment
Михал, ты написал, так как блоки досинхронизации почти всегда должны быть свободны от побочных эффектов. консольная печать нарушит это свойство побочного эффекта? поэтому обновление таблицы базы данных сделает то же самое, хорошо? Что должно быть внутри блока dosync? Еще раз спасибо, отличный пост! - person CHAPa; 27.08.2010
comment
Верно, вывод на консоль и обновление таблицы базы данных считаются побочными эффектами. Блоки dosync, как правило, не должны содержать код побочного эффекта, за исключением вызовов функций модификации ссылок с учетом транзакций (alter / commute / ref-set), за тем важным исключением, что любые действия, отправляемые агентам, могут содержать побочные эффекты, которые должны произойти после завершения транзакции (все такие действия будут выполняться, если и когда транзакция будет зафиксирована, поэтому нет риска вызвать один и тот же побочный эффект более одного раза). - person Michał Marczyk; 27.08.2010
comment
Таким образом, если вам нужно изменить значения пары ссылок и обновить базу данных, ваш dosync будет содержать вызовы alter/commute для изменения ссылок и send/send-off, говорящие агенту выполнить обновление базы данных после фиксации транзакции (и поэтому больше не возникает риск повторной попытки или сбоя из-за достижения предела повторных попыток). - person Michał Marczyk; 27.08.2010
comment
Да, и еще о почти... отладочные распечатки в транзакциях могут быть так же полезны, как и везде (в частности, они сообщат вам, сколько раз ваша транзакция должна быть повторена, как прекрасно демонстрирует код спящего парикмахера Лау); вызовы запоминаемых функций из транзакций, конечно, должны вызывать побочный эффект записи любых вновь вычисленных значений; и т. д. Тем не менее, нужно быть действительно уверенным в том, что вы делаете, когда вводите побочные эффекты внутри dosync - много поломок может произойти из-за отсутствия заботы в этом вопросе. - person Michał Marczyk; 27.08.2010
comment
Спасибо Михал. Отличный вид! Действительно мило ! - person CHAPa; 27.08.2010
comment
Когда вы send-off запускаете агент из dosync, он не запускается, пока не закончится dosync. Это означает, что слишком поздно откатывать транзакцию STM, если что-то пойдет не так в вашем агенте, что всегда возможно, если ваш агент обращается к базам данных (база данных может отклонить ваши данные или сервер может быть недоступен). Я думаю, что координация реферала и базы данных в настоящее время очень сложна (или невозможна?) даже с использованием агентов. - person Brian Carper; 28.08.2010
comment
О, это совершенно верно...! Спасибо за комментарий. Это только подтверждает мое подозрение, что мой STM-fu немного заржавел... - person Michał Marczyk; 28.08.2010

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

person Community    schedule 27.08.2010
comment
Да ! Операции чтения не требуют блокировки. это без блокировки. когда вы читаете, вы получаете снимок. Но если вы пишете, операция фиксируется только в том случае, если снимок равен тому, что у вас есть. если при попытке записи данные не совпадают, выполняется откат операции. - person CHAPa; 27.08.2010

Чтобы дать полную картину тем, кто ищет, у Clojure есть аналог synchronized. Это полезно, когда для взаимодействия приходится работать с типами Java, не поддерживающими многопотоковое исполнение.

(locking x & body)
person jartur    schedule 03.11.2010

основное отличие следующее

Clojure STM поддерживает оптимистичный параллелизм, тогда как синхронизация JAVA — пессимистичный

Clojure STM не получает блокировку, пока не будет создано более одного потока. если изменяемое состояние обновляется другим потоком, то операция внутри dosync повторяется. Кроме того, dosync является обязательным для изменяемых состояний. Clojure выдает исключение invalidState, когда dosync отсутствует, в отличие от JAVA.

person Amrish Pandey    schedule 10.04.2016
comment
Что ж, это совершенно разные типы поддержки параллелизма (транзакционный доступ к изменяемым значениям и реализация критических разделов с использованием synchronized), поэтому их нельзя сравнивать. Если бы в Java была реализована STM с двухфазной блокировкой, тогда можно было бы сравнить и сказать, что это «оптимистичный» протокол (а не «оптимистичный параллелизм», что звучит как вечеринка), и это «пессимистический» протокол. Это вещь СУБД! И дело даже не в STM, а в его реализации MVCC в Clojure. - person David Tonhofer; 26.08.2019