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