Учебники

Clojure — параллельное программирование

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

Программная система транзакционной памяти (STM), предоставляемая посредством dosync, ref, set, alter и т. Д., Поддерживает совместное использование изменяющегося состояния между потоками синхронным и скоординированным образом. Система агентов поддерживает совместное использование изменяющегося состояния между потоками асинхронным и независимым способом. Система атомов поддерживает разделение изменяющегося состояния между потоками синхронно и независимо. Принимая во внимание, что динамическая система var, доступная через def, привязку и т. Д., Поддерживает изоляцию изменяющегося состояния внутри потоков.

Другие языки программирования также следуют модели параллельного программирования.

  • Они имеют прямую ссылку на данные, которые могут быть изменены.

  • Если требуется общий доступ, объект блокируется, значение изменяется, и процесс продолжается для следующего доступа к этому значению.

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

Если требуется общий доступ, объект блокируется, значение изменяется, и процесс продолжается для следующего доступа к этому значению.

В Clojure нет блокировок, но есть косвенные ссылки на неизменяемые постоянные структуры данных.

В Clojure есть три типа ссылок.

  • Изменения — изменения изолированы в потоках.

  • Refs — Изменения синхронизируются и координируются между потоками.

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

Изменения — изменения изолированы в потоках.

Refs — Изменения синхронизируются и координируются между потоками.

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

Следующие операции возможны в Clojure в отношении параллельного программирования.

операции

Параллелизм в Clojure основан на транзакциях. Ссылки могут быть изменены только внутри транзакции. Следующие правила применяются в транзакциях.

  • Все изменения являются атомарными и изолированными.
  • Каждое изменение ссылки происходит в транзакции.
  • Ни одна транзакция не видит эффект от другой транзакции.
  • Все транзакции размещаются внутри блока dosync.

Мы уже видели, что делает блок dosync, давайте посмотрим на него еще раз.

dosync

Запускает выражение (в неявном do) в транзакции, которая включает выражение и любые вложенные вызовы. Запускает транзакцию, если в этом потоке еще не запущено ни одного. Любое необработанное исключение прервет транзакцию и выйдет из режима досинхронизации.

Ниже приводится синтаксис.

Синтаксис

(dosync expression)

Параметры — ‘expression’ — это набор выражений, которые будут входить в блок dosync.

Возвращаемое значение — Нет.

Давайте рассмотрим пример, в котором мы пытаемся изменить значение ссылочной переменной.

пример

Live Demo

(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, как это сделано в следующей программе.

пример

Live Demo

(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.

пример

Live Demo

(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. Если транзакция успешна, оба значения изменятся, иначе вся транзакция не удастся.

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