В программировании Clojure большинство типов данных являются неизменяемыми, поэтому, когда речь идет о параллельном программировании, код, использующий эти типы данных, довольно безопасен, когда код выполняется на нескольких процессорах. Но часто требуется совместное использование данных, и когда дело доходит до обмена данными между несколькими процессорами, возникает необходимость обеспечить сохранение состояния данных с точки зрения целостности при работе с несколькими процессорами. Это известно как параллельное программирование, и Clojure обеспечивает поддержку такого программирования.
Программная система транзакционной памяти (STM), предоставляемая посредством dosync, ref, set, alter и т. Д., Поддерживает совместное использование изменяющегося состояния между потоками синхронным и скоординированным образом. Система агентов поддерживает совместное использование изменяющегося состояния между потоками асинхронным и независимым способом. Система атомов поддерживает разделение изменяющегося состояния между потоками синхронно и независимо. Принимая во внимание, что динамическая система var, доступная через def, привязку и т. Д., Поддерживает изоляцию изменяющегося состояния внутри потоков.
Другие языки программирования также следуют модели параллельного программирования.
-
Они имеют прямую ссылку на данные, которые могут быть изменены.
-
Если требуется общий доступ, объект блокируется, значение изменяется, и процесс продолжается для следующего доступа к этому значению.
Они имеют прямую ссылку на данные, которые могут быть изменены.
Если требуется общий доступ, объект блокируется, значение изменяется, и процесс продолжается для следующего доступа к этому значению.
В Clojure нет блокировок, но есть косвенные ссылки на неизменяемые постоянные структуры данных.
В Clojure есть три типа ссылок.
-
Изменения — изменения изолированы в потоках.
-
Refs — Изменения синхронизируются и координируются между потоками.
-
Агенты — включает асинхронные независимые изменения между потоками.
Изменения — изменения изолированы в потоках.
Refs — Изменения синхронизируются и координируются между потоками.
Агенты — включает асинхронные независимые изменения между потоками.
Следующие операции возможны в Clojure в отношении параллельного программирования.
операции
Параллелизм в Clojure основан на транзакциях. Ссылки могут быть изменены только внутри транзакции. Следующие правила применяются в транзакциях.
- Все изменения являются атомарными и изолированными.
- Каждое изменение ссылки происходит в транзакции.
- Ни одна транзакция не видит эффект от другой транзакции.
- Все транзакции размещаются внутри блока dosync.
Мы уже видели, что делает блок dosync, давайте посмотрим на него еще раз.
dosync
Запускает выражение (в неявном do) в транзакции, которая включает выражение и любые вложенные вызовы. Запускает транзакцию, если в этом потоке еще не запущено ни одного. Любое необработанное исключение прервет транзакцию и выйдет из режима досинхронизации.
Ниже приводится синтаксис.
Синтаксис
(dosync expression)
Параметры — ‘expression’ — это набор выражений, которые будут входить в блок dosync.
Возвращаемое значение — Нет.
Давайте рассмотрим пример, в котором мы пытаемся изменить значение ссылочной переменной.
пример
(ns clojure.examples.example (:gen-class)) (defn Example [] (def names (ref [])) (alter names conj "Mark")) (Example)
Выход
Приведенная выше программа при запуске выдает следующую ошибку.
Caused by: java.lang.IllegalStateException: No transaction running at clojure.lang.LockingTransaction.getEx(LockingTransaction.java:208) at clojure.lang.Ref.alter(Ref.java:173) at clojure.core$alter.doInvoke(core.clj:1866) at clojure.lang.RestFn.invoke(RestFn.java:443) at clojure.examples.example$Example.invoke(main.clj:5) at clojure.examples.example$eval8.invoke(main.clj:7) at clojure.lang.Compiler.eval(Compiler.java:5424) ... 12 more
Из ошибки ясно видно, что вы не можете изменить значение ссылочного типа без предварительной инициации транзакции.
Чтобы приведенный выше код работал, мы должны поместить команду alter в блок dosync, как это сделано в следующей программе.
пример
(ns clojure.examples.example (:gen-class)) (defn Example [] (def names (ref [])) (defn change [newname] (dosync (alter names conj newname))) (change "John") (change "Mark") (println @names)) (Example)
Вышеуказанная программа производит следующий вывод.
Выход
[John Mark]
Давайте посмотрим еще один пример dosync.
пример
(ns clojure.examples.example (:gen-class)) (defn Example [] (def var1 (ref 10)) (def var2 (ref 20)) (println @var1 @var2) (defn change-value [var1 var2 newvalue] (dosync (alter var1 - newvalue) (alter var2 + newvalue))) (change-value var1 var2 20) (println @var1 @var2)) (Example)
В приведенном выше примере у нас есть два значения, которые изменяются в блоке DOSINC. Если транзакция успешна, оба значения изменятся, иначе вся транзакция не удастся.
Вышеуказанная программа производит следующий вывод.