7가지 동시성 모델 4장

21
Separating Identity from State (The Clojure Way) 아꿈사 Cecil

Upload: hyeonseok-choi

Post on 13-Jan-2017

72 views

Category:

Technology


0 download

TRANSCRIPT

Page 1: 7가지 동시성 모델 4장

Separating�Identity�from�State�(The�Clojure�Way)

아꿈사�

Cecil

Page 2: 7가지 동시성 모델 4장

목�차

•원자(Atom)�

• Agent(에이전트)�

• STM(Software�Transaction�Memory)�

• 원자와�STM의�비교

Page 3: 7가지 동시성 모델 4장

순수�함수형�언어는�가변�데이터를�지원하지�않는다�

Clojure는�동시성을�염두에둔�가변�데이터를�지원�

(원자,�에이전트,�STM)

Page 4: 7가지 동시성 모델 4장

원자

user=>�(def�my-atom�(atom�42))���;�원자�정의�

#’user/my-atom�

user=>�(deref�my-atom)�����������������;�값�조회�

42��

user=>�@my-atom��������������������������;�값�조회�

42��

user=>�(swap!�my-atom�inc)����������;�원자에�inc�함수�적용�

43�

user=>�(swap!�my-atom�+�2)���������;�원자에�+�함수�적용�

45��

user=>�(reset!�my-atom�0)�������������;�원자의�값을�변경�

0

Page 5: 7가지 동시성 모델 4장

예제:�멀티스레딩�

(def�players�(atom�()))�����������������������������������;�원자�players�정의�

(defn�list-players�[]�������������������

(response�(json/encode�@players)))���������;�players를�조회�

(defn�create-player�[player-name]��

(swap!�players�conj�player-name)������������;�새로운�player�추가�

(status�(response�"")�201))��

(defroutes�app-routes�

(GET�"/players"�[]�(list-players))�

(PUT�"/players/:player-name"�[player-name]�(create-player�player-name)))��

(defn�-main�[&�args]�

(run-jetty�(site�app-routes)�{:port�3000}))�

players를�json으로�변환하는중��

새로운�player를�추가된다면?

클로저의�자료구조는�영속적�속성을�가지므로�스레드에�안전

Page 6: 7가지 동시성 모델 4장

클로저의�영속성�자료구조•여기서�영속성은�불변성과�유사한�의미�

• 값이�변경�되었을때�이전�값을�유지

user=>�(def�listv1�(list�1�2�3))�

�#’user/listv1�

user=>�listv1�

(1�2�3)��

;�새로운�항목�추가�

user=>�(def�listv2�(cons�4�listv1))�

#’user/listv2�

user=>�listv2�

(4�1�2�3)�

user=> (def mapv1 {:name "paul" :age 45})#'user/mapv1user=> (def mapv2 (assoc mapv1 :sex :male))#'user/mapv2user=> mapv1{:age 45, :name "paul"}user=> mapv2{:age 45, :name "paul", :sex :male}

Persistent data structures behave as though a complete copy is made eachtime they’re modified. If that were how they were actually implemented, theywould be very inefficient and therefore of limited use (like CopyOnWriteArrayList,which we saw in Copy on Write, on page 34). Happily, the implementation ismuch more clever than that and makes use of structure sharing.

The easiest persistent data structure to understand is the list. Here’s a simplelist:

user=> (def listv1 (list 1 2 3))#'user/listv1user=> listv1(1 2 3)

And here’s a diagram of what it looks like in memory:

1 2 3

listv1

Now let’s create a modified version with cons, which returns a copy of the listwith an item added to the front:

user=> (def listv2 (cons 4 listv1))#'user/listv2user=> listv2(4 1 2 3)

The new list can share all of the previous list—no copying necessary:

1 2 3

listv1

4

listv2

Finally, let’s create another modified version:

report erratum • discuss

Day 1: Atoms and Persistent Data Structures • 89

www.it-ebooks.info

user=> (def mapv1 {:name "paul" :age 45})#'user/mapv1user=> (def mapv2 (assoc mapv1 :sex :male))#'user/mapv2user=> mapv1{:age 45, :name "paul"}user=> mapv2{:age 45, :name "paul", :sex :male}

Persistent data structures behave as though a complete copy is made eachtime they’re modified. If that were how they were actually implemented, theywould be very inefficient and therefore of limited use (like CopyOnWriteArrayList,which we saw in Copy on Write, on page 34). Happily, the implementation ismuch more clever than that and makes use of structure sharing.

The easiest persistent data structure to understand is the list. Here’s a simplelist:

user=> (def listv1 (list 1 2 3))#'user/listv1user=> listv1(1 2 3)

And here’s a diagram of what it looks like in memory:

1 2 3

listv1

Now let’s create a modified version with cons, which returns a copy of the listwith an item added to the front:

user=> (def listv2 (cons 4 listv1))#'user/listv2user=> listv2(4 1 2 3)

The new list can share all of the previous list—no copying necessary:

1 2 3

listv1

4

listv2

Finally, let’s create another modified version:

report erratum • discuss

Day 1: Atoms and Persistent Data Structures • 89

www.it-ebooks.info

Page 7: 7가지 동시성 모델 4장

리스트�전체가�아닌�일부만�사용하는�경우�

복사를�통해�영속성�보장

user=>�(def�listv1�(list�1�2�3�4))�

#’user/listv1�

user=>�(def�listv2�(take�2�listv1))��

#'user/listv2��

user=>�listv2��

(1�2)�

user=> (def listv3 (cons 5 (rest listv1)))#'user/listv3user=> listv3(5 2 3)

1 2 3

listv1

4

listv2

5

listv3

In this instance, the new list only makes use of part of the original, butcopying is still not necessary.

We can’t always avoid copying. Lists handle only common tails well—if wewant to have two lists with different tails, we have no choice but to copy.Here’s an example:

user=> (def listv1 (list 1 2 3 4))#'user/listv1user=> (def listv2 (take 2 listv1))#'user/listv2user=> listv2(1 2)

This leads to the following in memory:

1 2 3

listv1

4

1 2

listv2

All of Clojure’s collections are persistent. Persistent vectors, maps, and setsare more complex to implement than lists, but for our purposes all we needto know is that they share structure and that they provide similar performancebounds to their nonpersistent equivalents in languages like Ruby and Java.

Chapter 4. The Clojure Way—Separating Identity from State • 90

report erratum • discusswww.it-ebooks.info

Page 8: 7가지 동시성 모델 4장

명령형�언어에서�변수(Identity)�는��

변화하는�값(상태)을�가지지만,�

클로저에서�한�번�상태는�변하지�않음�

영속성�보장

Page 9: 7가지 동시성 모델 4장

원자�etc.• 재시도�

• 내부적으로�Java�AtomicReference의�compareAndSet�메서드를�

사용함으로�재시도가�발생할�수�있음�

• 함수형�코드의�특성상�부작용이�생기지�않음�

• 확인자�및�감시자

user=>�(def�non-negative�(atom�0�:validator�#(>=�%�0)))���;�확인자�

user=>�(reset!�non-negative�-1)��

IllegalStateException�Invalid�reference�state��

user=>�(def�a�(atom�0))�

user=>�(add-watch�a�:print�#(println�"Changed�from�"�%3�"�to�"�%4))�;�감시자�

user=>�(swap!�a�+�2)�

Changed�from�0�to�2

Page 10: 7가지 동시성 모델 4장

에이전트

user=>�(def�my-agent�(agent�0))��������;�에이전트�정의�

user=>�@my-agent������������������������������;�값�조회�

0��

user=>�(send�my-agent�inc)����������������;�에이전트에�inc�함수�적용�

user=>�@my-agent�

1�

user=>�(send�my-agent�+�2)���������������;�에이전트에�+�함수�적용�

user=>�@my-agent�

3�

• 원자와의�차이점:�send�

• 에이전트의�값이�변하기�전에�바로�리턴�됨

Page 11: 7가지 동시성 모델 4장

• 동작이�완료되기를�기다리기�

• 에러�처리

에이전트�etc.

user=>�(def�my-agent�(agent�0))#'user/my-agentuser=>�(send�my-agent�#((Thread/sleep�2000)�(inc�%)))user=>�(await�my-agent)��������������������������������������������������������������;�동작이�완료되기를�기다림 niluser=>�@my-agent1�

user=>�(def�non-negative�(agent�0�:validator�(fn�[new-val]�(>=�new-val�0))))�������;�확인자�정의�

user=>�(send�non-negative�dec)�������������������������������������������������������������������������������;�-1이�되어�실패�상태가�됨.�

user=>�@non-negative���������������������������������������������������������������������������������������0�

user=>�(send�non-negative�inc)��������������������������������������������������������������������������������;�실패�상태에서는�값�변경�안됨������

IllegalStateException�Invalid�reference�state�clojure.lang.ARef.validate...�������������

user=>�(agent-error�non-negative)���������������������������������������������������������������������������;�에러�내용�조회�

#<IllegalStateException�java.lang.IllegalStateException:�Invalid�reference�state>��

user=>�(restart-agent�non-negative�0)����������������������������������������������������������������������;�에이전트�다시�시작�

user=>�(agent-error�non-negative)�

nil

Page 12: 7가지 동시성 모델 4장

예제:�인메모리�로그�

(def�log-entries�(agent�[]))��������������������������������������������;�로그�수집을�위한�Agent�

(defn�log�[entry]�������������������������������������������������������������

����(send�log-entries�conj�[(now)�entry]))����������������������;�agent로�로그�전송�

logger.core=>�(log�"Something�happened”)�

#<Agent@bd99597:�[[1366822537794�"Something�happened”]]>�

logger.core=>�(log�"Something�else�happened”)�

#<Agent@bd99597:�[[1366822538932�"Something�happened”]]>�

logger.core=>�@log-entries�

[[1366822537794�"Something�happened"]�[1366822538932�"Something�else�happened"]]�

Page 13: 7가지 동시성 모델 4장

• ref를�정의하고,�트랜잭션�내에서만�변경�가능

트랜잭션�메모리(STM)

user=>�(def�my-ref�(ref�0))������������������������������������;�ref�정의�

user=>�@my-ref�

0��

user=>�(ref-set�my-ref�42)�������������������������������������;�ref�값�설정�시도�

IllegalStateException�No�transaction�running��

user=>�(alter�my-ref�inc)����������������������������������������;�ref�함수�적용�시도�

IllegalStateException�No�transaction�running��

user=>�(dosync�(ref-set�my-ref�42))����������������������;�트랜잭션�실행�(dosync)�

user=>�@my-ref�

42��

user=>�(dosync�(alter�my-ref�inc))�

user=>�@my-ref�

43�

Page 14: 7가지 동시성 모델 4장

STM은�3가지�트랜잭션�속성을�지원�

원자성(Atomic)�

일관성(Consistent)�

고립성(Isolated)�

지속성(Durability)�지원�안함

Page 15: 7가지 동시성 모델 4장

트랜잭션�재시도•트랜잭션이�모순되는�변경을�감지할�경우�재시도�

• 에이전트�SEND는�트랙잭션이�성공할�경우에만�호출이�됨.

(def�attempts�(atom�0))�������;�재시도�횟수�기록�

(def�transfers�(agent�0))�������;�전송�횟수�기록�

(defn�transfer�[from�to�amount]��

����(dosync��

��������(swap!�attempts�inc)�//�Side-effect�

��������(send�transfers�inc)�

��������(alter�from�-�amount)�

��������(alter�to�+�amount)))�

(def�checking�(ref�10000))��

(def�savings�(ref�20000))��

(defn�stress-thread�[from�to�iterations�amount]����(Thread.�#(dotimes�[_�iterations]�(transfer�from�to�amount))))��

(defn�-main�[&�args]�

����(println�"Before:�Checking�="�@checking�"�Savings�="�@savings)��

����(let�[t1�(stress-thread�checking�savings�100�100)��

�����������t2�(stress-thread�savings�checking�200�100)]��

��������(.start�t1)�(.start�t2)�(.join�t1)�(.join�t2))��

(await�transfers)�

(println�"Attempts:�"�@attempts)���������������;�300�이상�

(println�"Transfers:�"�@transfers)����������������;�300��

(println�"After:�Checking�="�@checking�"�Savings�="�@savings))�

Page 16: 7가지 동시성 모델 4장

예제:�STM을�이용한�철학자(def�philosophers�(into�[]�(repeatedly�5�#(ref�:thinking))))��;�5명의�철학자�정의�

(defn�think�[]�(Thread/sleep�(rand�1000)))��

(defn�eat�[]��(Thread/sleep�(rand�1000)))��

(defn�philosopher-thread�[n]��

����(Thread.��#(let�[philosopher�(philosophers�n)�

������������������������������left�(philosophers�(mod�(-�n�1)�5))��������;�왼쪽�철학자�

������������������������������right�(philosophers�(mod�(+�n�1)�5))]����;�오른쪽�철학자�

����������������������(while�true�(think)��

�����������������������������(when�(claim-chopsticks�philosopher�left�right)�(eat)��;�양쪽�철학자�검사�

��������������������������������(release-chopsticks�philosopher))))))�

(defn�-main�[&�args]�

����(let�[threads�(map�philosopher-thread�(range�5))]��

��������(doseq�[thread�threads]�(.start�thread))��

��������(doseq�[thread�threads]�(.join�thread))))�

Page 17: 7가지 동시성 모델 4장

(defn�release-chopsticks�[philosopher]��

����(dosync�(ref-set�philosopher�:thinking)))��

(defn�claim-chopsticks�[philosopher�left�right]��

����(dosync��

��������(when�(and�(=�@left�:thinking)�(=�@right�:thinking))��

������������(ref-set�philosopher�:eating))))��

;�클로저의�STM은�서로�다른�트랜잭션이�동일한�ref를�겹쳐서�변경하지�않도록�보장�

;�위의�경우�다른�ref를�참조함으로,�인접한�철학자가�동시에�식사�가능�

;�이를�막기위해�ensure�사용,�ref의�리턴�값이�다른�트랜잭션에서�변경되지�않았음을�보장�

(defn�claim-chopsticks�[philosopher�left�right]��

����(dosync��

��������(when�(and�(=�(ensure�left)�:thinking)�(=�(ensure�right)�:thinking))��

������������(ref-set�philosopher�:eating))))��

Page 18: 7가지 동시성 모델 4장

예제:�원자를�이용한�철학자(def�philosophers�(atom�(into�[]�(repeat�5�:thinking))))��������;�5명의�철학자�리스트를�원자로��생성

(defn�philosopher-thread�[philosopher]��

����(Thread.�#(let�[left�(mod�(-�philosopher�1)�5)�right�(mod�(+�philosopher�1)�5)]��

��������(while�true�(think)��

������������(when�(claim-chopsticks!�philosopher�left�right)��

����������������(eat)��

������������(release-chopsticks!�philosopher))))))�

(defn�release-chopsticks!�[philosopher]�

����(swap!�philosophers�assoc�philosopher�:thinking))��

(defn�claim-chopsticks!�[philosopher�left�right]��

����(swap!�philosophers��

��������(fn�[ps]�(if�(and�(=�(ps�left)�:thinking)�(=�(ps�right)�:thinking))��

������������������������(assoc�ps�philosopher�:eating)�ps)))���������������;�해당�철학자의�값을�설정후�원자에�

����(=�(@philosophers�philosopher)�:eating))�

Page 19: 7가지 동시성 모델 4장

STM�vs.�원자

STM:�여러�값에�대해�조절되는�변경을�가능하게�함�

원자:�단일�값에�대한�독립적인�변경을�보장�

STM�해법을�원자로�바꾸는�것은�어렵지�않게�가능�

가변�데이터의�최소�사용을�위해�원자의�사용을�권장

Page 20: 7가지 동시성 모델 4장

정리

•동시성�관점에서의�클로저의�장단점�

• 장점:�함수형�언어이지만,�이를�벗어나는�해법도�제공�

• 단점:�분산�프로그래밍을�지원하지�않음

Page 21: 7가지 동시성 모델 4장

References

•Paul�Butcher,�7가지�동시성�모델(임백준�옮김).�서울

시�마포구�양화로�한빛미디어,�2016.�