Clojure-동시 프로그래밍

Clojure 프로그래밍에서 대부분의 데이터 유형은 변경 불가능하므로 동시 프로그래밍의 경우 이러한 데이터 유형을 사용하는 코드는 코드가 여러 프로세서에서 실행될 때 매우 안전합니다. 그러나 여러 번 데이터를 공유해야하는 요구 사항이 있으며 여러 프로세서에서 데이터를 공유하는 경우 여러 프로세서로 작업 할 때 데이터 상태가 무결성 측면에서 유지되는지 확인해야합니다. 이것은concurrent programming Clojure는 이러한 프로그래밍을 지원합니다.

dosync, ref, set, alter 등을 통해 노출되는 소프트웨어 트랜잭션 메모리 시스템 (STM)은 동기식 및 조정 된 방식으로 스레드간에 변경 상태 공유를 지원합니다. 에이전트 시스템은 비동기적이고 독립적 인 방식으로 스레드 간의 변경 상태 공유를 지원합니다. 원자 시스템은 동기적이고 독립적 인 방식으로 스레드 간의 변경 상태 공유를 지원합니다. def, binding 등을 통해 노출되는 동적 var 시스템은 스레드 내에서 상태 변경 격리를 지원합니다.

다른 프로그래밍 언어도 동시 프로그래밍 모델을 따릅니다.

  • 변경 될 수있는 데이터에 대한 직접적인 참조가 있습니다.

  • 공유 액세스가 필요한 경우 개체가 잠기고 값이 변경되며 해당 값에 대한 다음 액세스를 위해 프로세스가 계속됩니다.

Clojure에는 잠금이 없지만 변경 불가능한 영구 데이터 구조에 대한 간접 참조가 있습니다.

Clojure에는 세 가지 유형의 참조가 있습니다.

  • Vars − 변경 사항은 스레드에서 격리됩니다.

  • Refs − 변경 사항은 스레드간에 동기화되고 조정됩니다.

  • Agents − 스레드 간의 비동기 독립 변경을 포함합니다.

동시 프로그래밍과 관련하여 Clojure에서 다음 작업이 가능합니다.

업무

Clojure의 동시성은 트랜잭션을 기반으로합니다. 참조는 트랜잭션 내에서만 변경할 수 있습니다. 거래에는 다음과 같은 규칙이 적용됩니다.

  • 모든 변경은 원자 적이며 격리되어 있습니다.
  • 참조에 대한 모든 변경은 트랜잭션에서 발생합니다.
  • 어떤 거래도 다른 거래의 효과를 보지 못합니다.
  • 모든 트랜잭션은 dosync 블록 내부에 배치됩니다.

우리는 이미 dosync 블록이하는 일을 보았습니다. 다시 살펴 보겠습니다.

dosync

표현식 및 중첩 된 호출을 포함하는 트랜잭션에서 표현식 (암시 적 do에서)을 실행합니다. 이 스레드에서 이미 실행중인 트랜잭션이 없으면 트랜잭션을 시작합니다. 잡히지 않은 예외는 트랜잭션을 중단하고 dosync에서 흘러 나옵니다.

다음은 구문입니다.

통사론

(dosync expression)

Parameters − 'expression'은 dosync 블록에 들어올 표현식 집합입니다.

Return Value − 없음.

참조 변수의 값을 변경하는 예를 살펴 보겠습니다.

(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

오류를 통해 먼저 트랜잭션을 시작하지 않으면 참조 유형의 값을 변경할 수 없음을 분명히 알 수 있습니다.

위의 코드가 작동하려면 다음 프로그램에서 수행 한 것처럼 dosync 블록에 alter 명령을 배치해야합니다.

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

위의 예에서는 dosync 블록에서 변경되는 두 개의 값이 있습니다. 트랜잭션이 성공하면 두 값이 모두 변경되고 그렇지 않으면 전체 트랜잭션이 실패합니다.

위의 프로그램은 다음과 같은 출력을 생성합니다.

산출

10 20
-10 40