SlideShare une entreprise Scribd logo
1  sur  46
難しそうで
  難しくない
  少し難しい
Clojure並行処理
角田直行 <kakuda@gmail.com>
はじめに
• 今回はClojureで用意されている
 スレッド処理、並行処理について

• Clojureの基本文法が分かっている
 ことが前提

• 桃屋のやつはまだ食べた事ないス
 (辛いのが苦手な人でも大丈夫?)
背景
• 時代はマルチコア
• 分散(Hadoop)なんて流行んない
                (嘘




 • そんなにサーバもデータも無い
• 1台で最大限のパフォーマンスを求める
 のが現実的
マルチスレッド
マルチスレッド

• はっきり言って難しい
 • 「The Art∼」は原理編で挫折中
• java.util.concurrent APIはスゴいけど
 まだまだ敷居が高い
そこで
Clojure
Clojure Concurrency

• Immutable(不変)な設計
• Software Transactional Memory
• プロセス間は対象外
• Java Concurrent APIも使える
Immutable
(def book {:title "1Q84" :author "村上春樹"})

(assoc book :publisher "新潮社")
; => {:publisher "新潮社", :title "1Q84", :author "村上春樹"}

book
;=> {:title "1Q84", :author "村上春樹"}
4つの型

• Var
• Ref
• Atom
• Agent
Var
Var

• mutable(可変)
• defで値を束縛
• bindingマクロでThreadローカルな
 値を束縛可能
bindingマクロ
(def v 1)

(println v) ; -> 1

(binding [v 23] (println v)) ;-> 23

(do
  (binding [v 456]
   (println v))
  (println v))
; -> 456
; 1
Ref
Ref
• 基本は変更不可
• トランザクション内で値を変更可能
 • STM
 • 自動で再実行
 • 副作用に注意 (io!マクロで防御)
Ref

(def cd (ref {:title "Orchestrion" :artist "Pat Metheny"}))

cd
; => #<Ref@26b496d: {:title "Orchestrion", :artist "Pat Metheny"}>

@cd
; => {:title "Orchestrion", :artist "Pat Metheny"}

                   値を見るには@をつける
                     (リーダマクロ)
ref-set
(ref-set cd {:price 2680 :genre "Jazz"})
; => java.lang.IllegalStateException: No transaction
running (NO_SOURCE_FILE:0)

(dosync (ref-set cd {:price 2680 :genre "Jazz"}))
; => {:price 2680, :genre "Jazz"}

@cd
; => {:price 2680, :genre "Jazz"}

          別の情報をセットする際に使う
alter
(alter cd assoc :year 2010)
; => java.lang.IllegalStateException: No transaction
running (NO_SOURCE_FILE:0)

(dosync (alter cd assoc :year 2010))
; => {:year 2010, :title "Orchestrion", :artist "Pat Metheny"}

@cd
; => {:year 2010, :title "Orchestrion", :artist "Pat Metheny"}

           前の情報を更新する際に使う
commute

(dosync (commute cd assoc :year 2010))
; => {:year 2010, :title "Orchestrion", :artist "Pat Metheny"}

@cd
; => {:year 2010, :title "Orchestrion", :artist "Pat Metheny"}


                  alterと何が違う?
alter or commute
• 基本alterを使え
• alterは、トランザクション中で
 指定された順序で実行する

 • commuteは順序は気にしない
• パフォーマンスはcommuteが上
alter                                   alter
                            (def logs (ref ""))

(with-new-thread                      (with-new-thread
 (dosync                               (dosync
  (println "--スレッドA 更新開始:" @logs)       (println "****スレッドB 更新開始:" @logs)
 (alter logs str "A")                   (Thread/sleep 1000)
 (println "--スレッドA 更新完了:" @logs)        (alter logs str "B")
 (Thread/sleep 4000)                    (println "****スレッドB 更新完了:" @logs)
 (println "--スレッドA 終了:" @logs)))        (println "****スレッドB 終了:" @logs)))


                     Aのトランザクション中
                      Bはリトライし続ける
--スレッドA 更新開始:
--スレッドA 更新完了: A      Aのトランザクションは
****スレッドB 更新開始:      終わってないので空のまま
****スレッドB 更新開始:      alterしようとしたら
****スレッドB 更新開始:      Aが終わってないので
****スレッドB 更新開始:      再実行
--スレッドA 終了: A
****スレッドB 更新開始: A    Aのトランザクションが
****スレッドB 更新完了: AB   終わって値が反映
****スレッドB 終了: AB
alter                            commute
                            (def logs (ref ""))

(with-new-thread                      (with-new-thread
 (dosync                               (dosync
  (println "--スレッドA 更新開始:" @logs)       (println "****スレッドB 更新開始:" @logs)
 (alter logs str "A")                   (Thread/sleep 1000)
 (println "--スレッドA 更新完了:" @logs)        (commute logs str "B")
 (Thread/sleep 4000)                    (println "****スレッドB 更新完了:" @logs)
 (println "--スレッドA 終了:" @logs)))        (println "****スレッドB 終了:" @logs)))


                 Aのトランザクション中でも
                    Bは更新を行う
--スレッドA 更新開始:
--スレッドA 更新完了: A
****スレッドB 更新開始:
                     commuteは更新順序を気
****スレッドB 更新完了: B
                     にしないので更新する
****スレッドB 終了: B
****スレッドB 更新開始:      Aが終わってないので再実行
****スレッドB 更新完了: B
****スレッドB 終了: B
****スレッドB 更新開始:
****スレッドB 更新完了: B
****スレッドB 終了: B
****スレッドB 更新開始:
--スレッドA 終了: A
                     Aのトランザクションが
****スレッドB 更新完了: AB
                     終わって値が反映
****スレッドB 終了: AB
commute                                       alter
                            (def logs (ref ""))

(with-new-thread                      (with-new-thread
 (dosync                               (dosync
  (println "--スレッドA 更新開始:" @logs)       (println "****スレッドB 更新開始:" @logs)
 (commute logs str "A")                 (Thread/sleep 1000)
 (println "--スレッドA 更新完了:" @logs)        (alter logs str "B")
 (Thread/sleep 4000)                    (println "****スレッドB 更新完了:" @logs)
 (println "--スレッドA 終了:" @logs)))        (println "****スレッドB 終了:" @logs)))


                        スレッドBが
                     commuteの場合も同様
--スレッドA 更新開始:
--スレッドA 更新完了: A
****スレッドB 更新開始:
****スレッドB 更新完了: B
****スレッドB 終了: B
--スレッドA 終了: A


@logs
;=> "BA"
ensure
                            (def logs (ref ""))
                                      (with-new-thread
(with-new-thread
                                       (dosync
 (dosync
                                        (ensure logs)
  (println "--スレッドA 更新開始:" @logs)
                                        (println "****スレッドB 更新開始:" @logs)
 (alter logs str "A")
                                        (Thread/sleep 1000)
 (println "--スレッドA 更新完了:" @logs)
                                        (alter logs str "B")
 (Thread/sleep 4000)                    (println "****スレッドB 更新完了:" @logs)
 (println "--スレッドA 終了:" @logs)))
                                        (println "****スレッドB 終了:" @logs)))



                         変更から保護する
ensure

--スレッドA 更新開始:
--スレッドA 更新完了: A
                     Aが終わるまで
--スレッドA 終了: A
                     Bはensureにより待機
****スレッドB 更新開始: A
****スレッドB 更新完了: AB
****スレッドB 終了: AB
Atom
Atom
• Refに似ている
• 独立した値を管理
 • 他に依存する値がない前提なので
  トランザクションは必要としない

• swap!は再実行される(副作用禁止!)
Atom

(def song (atom {:title "リハーサル" :artist "近藤夏子"}))

song
;=> #<Atom@5e54777e: {:title "リハーサル", :artist "近藤夏子"}>

@song
;=> {:title "リハーサル", :artist "近藤夏子"}


                        Refと同様
reset!

(reset! song {:title "sad to say" :artist "Jasmine"})
;=> {:title "sad to say", :artist "Jasmine"}

@song
;=> {:title "sad to say", :artist "Jasmine"}

         トランザクションは利用しないので
            dosync等は要らない
swap!
@song
;=> {:title "リハーサル", :artist "近藤夏子"}

(swap! song assoc :title "リアルでゴメン...")
;=> {:title "リアルでゴメン...", :artist "近藤夏子"}

@song
;=> {:title "リアルでゴメン...", :artist "近藤夏子"}

         内部で (AtomicReferenceの)
        Compare-And-Setを行っている
Agent
Agent
• 個々で値を管理
• (Erlang/Scalaの)Actorっぽい
 • 分散(プロセス間通信)はしない
• 変更は非同期に行われる
 • 結果 or エラー取得は問い合わせ必要
Agent

(def fetcher (agent “”))

fetcher
;=> #<Agent@39edd9b3: "">

@fetcher
;=> “”

                           Refと同様
send
(use '[clojure.contrib.io :only (slurp*)])
(defn fetch-title
 [_ url]
 (Thread/sleep 2000)
 (second (re-find #"<title>(.*?)</title>" (slurp* url))))

(send fetcher fetch-title "http://bit.ly/")
;=> #<Agent@39edd9b3: "">

@fetcher
;=> "bit.ly, a simple url shortener"

      sendで渡す関数の第1引数はagentの値
send

(do
 (send fetcher fetch-title "http://bit.ly/")
 (println "send 直後: " @fetcher)                send 直後:
 (Thread/sleep 1000)                           send 1秒後:
 (println "send 1秒後: " @fetcher)               send 4秒後: bit.ly, a simple url shortener
 (Thread/sleep 3000)
 (println "send 4秒後: " @fetcher))


                     直後、1秒後は関数の結果が
                      まだ求まっていないため
                       更新されていない
await, await-for
(do
 (send fetcher fetch-title "http://bit.ly/")
                                               await 直後: bit.ly, a simple url shortener
 (await fetcher)
 (println "await 直後: " @fetcher))

(do
 (send fetcher fetch-title "http://bit.ly/")
 (await-for 1500 fetcher)                      await-for 直後:
 (println "await-for 直後: " @fetcher)           await-for 3秒後: bit.ly, a simple url shortener
 (Thread/sleep 3000)
 (println "await-for 3秒後: " @fetcher))


            await: 結果が返るまで待つ
          await-for: 最低タイムアウト(ms)待つ
連続実行
(time
 (do
   (send fetcher fetch-title "http://www.google.com/")
   (send fetcher fetch-title "http://www.apple.com/")
   (send fetcher fetch-title "http://www.yahoo.com/")
   (await fetcher)
   (println "終了: " @fetcher)))
; -> 終了: Yahoo!
;   "Elapsed time: 7892.725 msecs"
agent-errors

(send fetcher fetch-title "http://notfound/")
;=> #<Agent@39edd9b3: "">

fetcher
;=> #<Agent@39edd9b3 FAILED: "">

(agent-errors fetcher)
(#<UnknownHostException java.net.UnknownHostException: notfound>)



        (clear-agent-errors fetcher)でクリア
おさらい
 Var     Ref   Atom   Agent

 可変      不変    不変     不変

同スレッド内   共有    共有     共有

 同期      同期    同期     非同期

 協調      協調    自律     協調
まとめ

• 基本は変更しない(Immutablityを保つ)
• 変更する際は特性に応じて
 4種の型(Var, Ref, Atom, Agent)から
 選択する

• 分からない場合はちょっと書いて試す
参照資料

• プログラミングClojure
• Clojure 本家
• Clojure Concurrency (動画)
• Software Transactional Memory
ソース
• clojure.lang.LockingTransaction
  → STMのトランザクション処理

• clojure.lang.Ref
• clojure.lang.Agent
• clojure.lang.Atom
Q&A

といっても自分もまだ
知らないことだらけ
ご清聴ありがとう
 ございました!

Contenu connexe

Tendances

PostgreSQL - C言語によるユーザ定義関数の作り方
PostgreSQL - C言語によるユーザ定義関数の作り方PostgreSQL - C言語によるユーザ定義関数の作り方
PostgreSQL - C言語によるユーザ定義関数の作り方Satoshi Nagayasu
 
2017年春のPerl
2017年春のPerl2017年春のPerl
2017年春のPerlcharsbar
 
これだけMakefile (Basics of makefile)
これだけMakefile (Basics of makefile)これだけMakefile (Basics of makefile)
これだけMakefile (Basics of makefile)seichi23
 
10分で作る Node.js Auto Scale 環境 with CloudFormation
10分で作る Node.js Auto Scale 環境 with CloudFormation10分で作る Node.js Auto Scale 環境 with CloudFormation
10分で作る Node.js Auto Scale 環境 with CloudFormationKazuyuki Honda
 
入門core.async
入門core.async入門core.async
入門core.asyncsohta
 
What's Temporal model FuelPHP東京勉強会03
What's Temporal model FuelPHP東京勉強会03What's Temporal model FuelPHP東京勉強会03
What's Temporal model FuelPHP東京勉強会03Takayuki Yamaguchi
 
Mongo dbのgridfsについて
Mongo dbのgridfsについてMongo dbのgridfsについて
Mongo dbのgridfsについてMasahiro Saito
 
Docker で xxxxxxサーバ を つくれませんでした
Docker で xxxxxxサーバ を つくれませんでしたDocker で xxxxxxサーバ を つくれませんでした
Docker で xxxxxxサーバ を つくれませんでしたkrs_mizuno
 
2017年夏のPerl
2017年夏のPerl2017年夏のPerl
2017年夏のPerlcharsbar
 
Aerospike紹介-LT用
Aerospike紹介-LT用Aerospike紹介-LT用
Aerospike紹介-LT用Joongjin Bae
 
Varnish 4.0 Release Party in Tokyo発表資料
Varnish 4.0 Release Party in Tokyo発表資料Varnish 4.0 Release Party in Tokyo発表資料
Varnish 4.0 Release Party in Tokyo発表資料Iwana Chan
 
『How to build a High Performance PSGI/Plack Server』のその後と ISUCON3を受けての話題
『How to build a High Performance PSGI/Plack Server』のその後と ISUCON3を受けての話題『How to build a High Performance PSGI/Plack Server』のその後と ISUCON3を受けての話題
『How to build a High Performance PSGI/Plack Server』のその後と ISUCON3を受けての話題Masahiro Nagano
 
GNU awk (gawk) を用いた Apache ログ解析方法
GNU awk (gawk) を用いた Apache ログ解析方法GNU awk (gawk) を用いた Apache ログ解析方法
GNU awk (gawk) を用いた Apache ログ解析方法博文 斉藤
 
初めてのPerl
初めてのPerl初めてのPerl
初めてのPerlarvelt s
 
.NET Web プログラミングにおける非同期 IO のすべて (Build Insider OFFLINE)
.NET Web プログラミングにおける非同期 IO のすべて (Build Insider OFFLINE).NET Web プログラミングにおける非同期 IO のすべて (Build Insider OFFLINE)
.NET Web プログラミングにおける非同期 IO のすべて (Build Insider OFFLINE)Tusyoshi Matsuzaki
 

Tendances (20)

PostgreSQL - C言語によるユーザ定義関数の作り方
PostgreSQL - C言語によるユーザ定義関数の作り方PostgreSQL - C言語によるユーザ定義関数の作り方
PostgreSQL - C言語によるユーザ定義関数の作り方
 
2017年春のPerl
2017年春のPerl2017年春のPerl
2017年春のPerl
 
これだけMakefile (Basics of makefile)
これだけMakefile (Basics of makefile)これだけMakefile (Basics of makefile)
これだけMakefile (Basics of makefile)
 
10分で作る Node.js Auto Scale 環境 with CloudFormation
10分で作る Node.js Auto Scale 環境 with CloudFormation10分で作る Node.js Auto Scale 環境 with CloudFormation
10分で作る Node.js Auto Scale 環境 with CloudFormation
 
入門core.async
入門core.async入門core.async
入門core.async
 
Scala on Hadoop
Scala on HadoopScala on Hadoop
Scala on Hadoop
 
What's Temporal model FuelPHP東京勉強会03
What's Temporal model FuelPHP東京勉強会03What's Temporal model FuelPHP東京勉強会03
What's Temporal model FuelPHP東京勉強会03
 
Mongo dbのgridfsについて
Mongo dbのgridfsについてMongo dbのgridfsについて
Mongo dbのgridfsについて
 
Docker で xxxxxxサーバ を つくれませんでした
Docker で xxxxxxサーバ を つくれませんでしたDocker で xxxxxxサーバ を つくれませんでした
Docker で xxxxxxサーバ を つくれませんでした
 
Glibc malloc internal
Glibc malloc internalGlibc malloc internal
Glibc malloc internal
 
Slub data structure
Slub data structureSlub data structure
Slub data structure
 
2017年夏のPerl
2017年夏のPerl2017年夏のPerl
2017年夏のPerl
 
Aerospike紹介-LT用
Aerospike紹介-LT用Aerospike紹介-LT用
Aerospike紹介-LT用
 
Varnish 4.0 Release Party in Tokyo発表資料
Varnish 4.0 Release Party in Tokyo発表資料Varnish 4.0 Release Party in Tokyo発表資料
Varnish 4.0 Release Party in Tokyo発表資料
 
『How to build a High Performance PSGI/Plack Server』のその後と ISUCON3を受けての話題
『How to build a High Performance PSGI/Plack Server』のその後と ISUCON3を受けての話題『How to build a High Performance PSGI/Plack Server』のその後と ISUCON3を受けての話題
『How to build a High Performance PSGI/Plack Server』のその後と ISUCON3を受けての話題
 
前期講座09
前期講座09前期講座09
前期講座09
 
Akka stream
Akka streamAkka stream
Akka stream
 
GNU awk (gawk) を用いた Apache ログ解析方法
GNU awk (gawk) を用いた Apache ログ解析方法GNU awk (gawk) を用いた Apache ログ解析方法
GNU awk (gawk) を用いた Apache ログ解析方法
 
初めてのPerl
初めてのPerl初めてのPerl
初めてのPerl
 
.NET Web プログラミングにおける非同期 IO のすべて (Build Insider OFFLINE)
.NET Web プログラミングにおける非同期 IO のすべて (Build Insider OFFLINE).NET Web プログラミングにおける非同期 IO のすべて (Build Insider OFFLINE)
.NET Web プログラミングにおける非同期 IO のすべて (Build Insider OFFLINE)
 

En vedette

ClojureではじめるSTM入門
ClojureではじめるSTM入門ClojureではじめるSTM入門
ClojureではじめるSTM入門sohta
 
Cẩm nang doanh nghiệp toàn cầu hóa với facebook (share bởi anhthien8)
Cẩm nang doanh nghiệp toàn cầu hóa với facebook (share bởi anhthien8)Cẩm nang doanh nghiệp toàn cầu hóa với facebook (share bởi anhthien8)
Cẩm nang doanh nghiệp toàn cầu hóa với facebook (share bởi anhthien8)Duong Anh Thien
 
Presentazione del Dipartimento Politiche Coesione su Accordo di Partenariato ...
Presentazione del Dipartimento Politiche Coesione su Accordo di Partenariato ...Presentazione del Dipartimento Politiche Coesione su Accordo di Partenariato ...
Presentazione del Dipartimento Politiche Coesione su Accordo di Partenariato ...Parma Couture
 
NPGBC - Monthly Theme Commentary - May 2016
NPGBC - Monthly Theme Commentary - May 2016NPGBC - Monthly Theme Commentary - May 2016
NPGBC - Monthly Theme Commentary - May 2016newpineygrove
 
Google Analytics Attribution
Google Analytics AttributionGoogle Analytics Attribution
Google Analytics AttributionMashMetrics
 
Auto insurance narrated show
Auto insurance narrated showAuto insurance narrated show
Auto insurance narrated showxpaperbackwriterx
 
150811pbdesignthinking 150811053102 lva1 app6892
150811pbdesignthinking 150811053102 lva1 app6892150811pbdesignthinking 150811053102 lva1 app6892
150811pbdesignthinking 150811053102 lva1 app6892Vera Kovaleva
 
Eurordis. enfermedades raras.
Eurordis. enfermedades raras.Eurordis. enfermedades raras.
Eurordis. enfermedades raras.José María
 
Protectfromrobots 151126145914-lva1-app6892
Protectfromrobots 151126145914-lva1-app6892Protectfromrobots 151126145914-lva1-app6892
Protectfromrobots 151126145914-lva1-app6892Vera Kovaleva
 
Breaktherules 150909013617-lva1-app6892
Breaktherules 150909013617-lva1-app6892Breaktherules 150909013617-lva1-app6892
Breaktherules 150909013617-lva1-app6892Vera Kovaleva
 
Kourtney Kelty's Resume 2015
Kourtney Kelty's Resume 2015Kourtney Kelty's Resume 2015
Kourtney Kelty's Resume 2015Kourtney Kelty
 
Clientshare Academy Briefing by Practice Paradox
Clientshare Academy Briefing by Practice ParadoxClientshare Academy Briefing by Practice Paradox
Clientshare Academy Briefing by Practice ParadoxPractice Paradox
 
Grafico diario del dax perfomance index para el 13 03-2013
Grafico diario del dax perfomance index para el 13 03-2013Grafico diario del dax perfomance index para el 13 03-2013
Grafico diario del dax perfomance index para el 13 03-2013Experiencia Trading
 
Genymotion 2.0 설치 가이드
Genymotion 2.0 설치 가이드Genymotion 2.0 설치 가이드
Genymotion 2.0 설치 가이드YoungSu Son
 
Statement of cost sheet
Statement of cost sheetStatement of cost sheet
Statement of cost sheetSubbu Pullela
 

En vedette (20)

ClojureではじめるSTM入門
ClojureではじめるSTM入門ClojureではじめるSTM入門
ClojureではじめるSTM入門
 
Cẩm nang doanh nghiệp toàn cầu hóa với facebook (share bởi anhthien8)
Cẩm nang doanh nghiệp toàn cầu hóa với facebook (share bởi anhthien8)Cẩm nang doanh nghiệp toàn cầu hóa với facebook (share bởi anhthien8)
Cẩm nang doanh nghiệp toàn cầu hóa với facebook (share bởi anhthien8)
 
Presentazione del Dipartimento Politiche Coesione su Accordo di Partenariato ...
Presentazione del Dipartimento Politiche Coesione su Accordo di Partenariato ...Presentazione del Dipartimento Politiche Coesione su Accordo di Partenariato ...
Presentazione del Dipartimento Politiche Coesione su Accordo di Partenariato ...
 
NPGBC - Monthly Theme Commentary - May 2016
NPGBC - Monthly Theme Commentary - May 2016NPGBC - Monthly Theme Commentary - May 2016
NPGBC - Monthly Theme Commentary - May 2016
 
diane_Nouns
diane_Nounsdiane_Nouns
diane_Nouns
 
Google Analytics Attribution
Google Analytics AttributionGoogle Analytics Attribution
Google Analytics Attribution
 
Vivega builders
Vivega buildersVivega builders
Vivega builders
 
about PEACE
about PEACEabout PEACE
about PEACE
 
Auto insurance narrated show
Auto insurance narrated showAuto insurance narrated show
Auto insurance narrated show
 
150811pbdesignthinking 150811053102 lva1 app6892
150811pbdesignthinking 150811053102 lva1 app6892150811pbdesignthinking 150811053102 lva1 app6892
150811pbdesignthinking 150811053102 lva1 app6892
 
Eurordis. enfermedades raras.
Eurordis. enfermedades raras.Eurordis. enfermedades raras.
Eurordis. enfermedades raras.
 
Protectfromrobots 151126145914-lva1-app6892
Protectfromrobots 151126145914-lva1-app6892Protectfromrobots 151126145914-lva1-app6892
Protectfromrobots 151126145914-lva1-app6892
 
Priortoyourpitch
PriortoyourpitchPriortoyourpitch
Priortoyourpitch
 
Breaktherules 150909013617-lva1-app6892
Breaktherules 150909013617-lva1-app6892Breaktherules 150909013617-lva1-app6892
Breaktherules 150909013617-lva1-app6892
 
Kourtney Kelty's Resume 2015
Kourtney Kelty's Resume 2015Kourtney Kelty's Resume 2015
Kourtney Kelty's Resume 2015
 
Clientshare Academy Briefing by Practice Paradox
Clientshare Academy Briefing by Practice ParadoxClientshare Academy Briefing by Practice Paradox
Clientshare Academy Briefing by Practice Paradox
 
Grafico diario del dax perfomance index para el 13 03-2013
Grafico diario del dax perfomance index para el 13 03-2013Grafico diario del dax perfomance index para el 13 03-2013
Grafico diario del dax perfomance index para el 13 03-2013
 
Genymotion 2.0 설치 가이드
Genymotion 2.0 설치 가이드Genymotion 2.0 설치 가이드
Genymotion 2.0 설치 가이드
 
Python - Module
Python - ModulePython - Module
Python - Module
 
Statement of cost sheet
Statement of cost sheetStatement of cost sheet
Statement of cost sheet
 

Similaire à 難しそうで難しくない少し難しいClojure並行処理

x86とコンテキストスイッチ
x86とコンテキストスイッチx86とコンテキストスイッチ
x86とコンテキストスイッチMasami Ichikawa
 
思ったほど怖くない! Haskell on JVM 超入門 #jjug_ccc #ccc_l8
思ったほど怖くない! Haskell on JVM 超入門 #jjug_ccc #ccc_l8思ったほど怖くない! Haskell on JVM 超入門 #jjug_ccc #ccc_l8
思ったほど怖くない! Haskell on JVM 超入門 #jjug_ccc #ccc_l8y_taka_23
 
Dart のコード自動生成の仕組みと、コード自動生成のパッケージを自作する方法について
Dart のコード自動生成の仕組みと、コード自動生成のパッケージを自作する方法についてDart のコード自動生成の仕組みと、コード自動生成のパッケージを自作する方法について
Dart のコード自動生成の仕組みと、コード自動生成のパッケージを自作する方法についてKosuke Saigusa
 
Lisp Tutorial for Pythonista : Day 3
Lisp Tutorial for Pythonista : Day 3Lisp Tutorial for Pythonista : Day 3
Lisp Tutorial for Pythonista : Day 3Ransui Iso
 
Mac_Terminal_ver1.0
Mac_Terminal_ver1.0Mac_Terminal_ver1.0
Mac_Terminal_ver1.0Satoshi Kume
 
tokyo.vcl発表資料(VarnishCache3.0新機能とVUPの仕方)
tokyo.vcl発表資料(VarnishCache3.0新機能とVUPの仕方)tokyo.vcl発表資料(VarnishCache3.0新機能とVUPの仕方)
tokyo.vcl発表資料(VarnishCache3.0新機能とVUPの仕方)Iwana Chan
 
Ekmett勉強会発表資料
Ekmett勉強会発表資料Ekmett勉強会発表資料
Ekmett勉強会発表資料時響 逢坂
 
Ekmett勉強会発表資料
Ekmett勉強会発表資料Ekmett勉強会発表資料
Ekmett勉強会発表資料時響 逢坂
 
Fabricでサーバー管理をDRYにしよう
Fabricでサーバー管理をDRYにしようFabricでサーバー管理をDRYにしよう
Fabricでサーバー管理をDRYにしようmax747
 
Webフレームワークを作ってる話 #osakapy
Webフレームワークを作ってる話 #osakapyWebフレームワークを作ってる話 #osakapy
Webフレームワークを作ってる話 #osakapyMasashi Shibata
 
第2回 JavaScriptから始めるプログラミング2016
第2回 JavaScriptから始めるプログラミング2016第2回 JavaScriptから始めるプログラミング2016
第2回 JavaScriptから始めるプログラミング2016kyoto university
 
ocamloptの全体像
ocamloptの全体像ocamloptの全体像
ocamloptの全体像Kiwamu Okabe
 
恋に落ちるデプロイツール
恋に落ちるデプロイツール恋に落ちるデプロイツール
恋に落ちるデプロイツールtotty jp
 

Similaire à 難しそうで難しくない少し難しいClojure並行処理 (20)

x86とコンテキストスイッチ
x86とコンテキストスイッチx86とコンテキストスイッチ
x86とコンテキストスイッチ
 
思ったほど怖くない! Haskell on JVM 超入門 #jjug_ccc #ccc_l8
思ったほど怖くない! Haskell on JVM 超入門 #jjug_ccc #ccc_l8思ったほど怖くない! Haskell on JVM 超入門 #jjug_ccc #ccc_l8
思ったほど怖くない! Haskell on JVM 超入門 #jjug_ccc #ccc_l8
 
Mysql casial01
Mysql casial01Mysql casial01
Mysql casial01
 
Dart のコード自動生成の仕組みと、コード自動生成のパッケージを自作する方法について
Dart のコード自動生成の仕組みと、コード自動生成のパッケージを自作する方法についてDart のコード自動生成の仕組みと、コード自動生成のパッケージを自作する方法について
Dart のコード自動生成の仕組みと、コード自動生成のパッケージを自作する方法について
 
Mina 20130417
Mina 20130417Mina 20130417
Mina 20130417
 
Lisp Tutorial for Pythonista : Day 3
Lisp Tutorial for Pythonista : Day 3Lisp Tutorial for Pythonista : Day 3
Lisp Tutorial for Pythonista : Day 3
 
Mac_Terminal_ver1.0
Mac_Terminal_ver1.0Mac_Terminal_ver1.0
Mac_Terminal_ver1.0
 
Vyatta 改造入門
Vyatta 改造入門Vyatta 改造入門
Vyatta 改造入門
 
tokyo.vcl発表資料(VarnishCache3.0新機能とVUPの仕方)
tokyo.vcl発表資料(VarnishCache3.0新機能とVUPの仕方)tokyo.vcl発表資料(VarnishCache3.0新機能とVUPの仕方)
tokyo.vcl発表資料(VarnishCache3.0新機能とVUPの仕方)
 
Ekmett勉強会発表資料
Ekmett勉強会発表資料Ekmett勉強会発表資料
Ekmett勉強会発表資料
 
PostgreSQL安定運用のコツ2009 @hbstudy#5
PostgreSQL安定運用のコツ2009 @hbstudy#5PostgreSQL安定運用のコツ2009 @hbstudy#5
PostgreSQL安定運用のコツ2009 @hbstudy#5
 
Ekmett勉強会発表資料
Ekmett勉強会発表資料Ekmett勉強会発表資料
Ekmett勉強会発表資料
 
Haskell超入門 Part.1
Haskell超入門 Part.1Haskell超入門 Part.1
Haskell超入門 Part.1
 
Stream2の基本
Stream2の基本Stream2の基本
Stream2の基本
 
Fabricでサーバー管理をDRYにしよう
Fabricでサーバー管理をDRYにしようFabricでサーバー管理をDRYにしよう
Fabricでサーバー管理をDRYにしよう
 
Webフレームワークを作ってる話 #osakapy
Webフレームワークを作ってる話 #osakapyWebフレームワークを作ってる話 #osakapy
Webフレームワークを作ってる話 #osakapy
 
第2回 JavaScriptから始めるプログラミング2016
第2回 JavaScriptから始めるプログラミング2016第2回 JavaScriptから始めるプログラミング2016
第2回 JavaScriptから始めるプログラミング2016
 
ocamloptの全体像
ocamloptの全体像ocamloptの全体像
ocamloptの全体像
 
計算機理論入門09
計算機理論入門09計算機理論入門09
計算機理論入門09
 
恋に落ちるデプロイツール
恋に落ちるデプロイツール恋に落ちるデプロイツール
恋に落ちるデプロイツール
 

Dernier

業務で生成AIを活用したい人のための生成AI入門講座(社外公開版:キンドリルジャパン社内勉強会:2024年4月発表)
業務で生成AIを活用したい人のための生成AI入門講座(社外公開版:キンドリルジャパン社内勉強会:2024年4月発表)業務で生成AIを活用したい人のための生成AI入門講座(社外公開版:キンドリルジャパン社内勉強会:2024年4月発表)
業務で生成AIを活用したい人のための生成AI入門講座(社外公開版:キンドリルジャパン社内勉強会:2024年4月発表)Hiroshi Tomioka
 
自分史上一番早い2024振り返り〜コロナ後、仕事は通常ペースに戻ったか〜 by IoT fullstack engineer
自分史上一番早い2024振り返り〜コロナ後、仕事は通常ペースに戻ったか〜 by IoT fullstack engineer自分史上一番早い2024振り返り〜コロナ後、仕事は通常ペースに戻ったか〜 by IoT fullstack engineer
自分史上一番早い2024振り返り〜コロナ後、仕事は通常ペースに戻ったか〜 by IoT fullstack engineerYuki Kikuchi
 
AWS の OpenShift サービス (ROSA) を使った OpenShift Virtualizationの始め方.pdf
AWS の OpenShift サービス (ROSA) を使った OpenShift Virtualizationの始め方.pdfAWS の OpenShift サービス (ROSA) を使った OpenShift Virtualizationの始め方.pdf
AWS の OpenShift サービス (ROSA) を使った OpenShift Virtualizationの始め方.pdfFumieNakayama
 
モーダル間の変換後の一致性とジャンル表を用いた解釈可能性の考察 ~Text-to-MusicとText-To-ImageかつImage-to-Music...
モーダル間の変換後の一致性とジャンル表を用いた解釈可能性の考察  ~Text-to-MusicとText-To-ImageかつImage-to-Music...モーダル間の変換後の一致性とジャンル表を用いた解釈可能性の考察  ~Text-to-MusicとText-To-ImageかつImage-to-Music...
モーダル間の変換後の一致性とジャンル表を用いた解釈可能性の考察 ~Text-to-MusicとText-To-ImageかつImage-to-Music...博三 太田
 
クラウドネイティブなサーバー仮想化基盤 - OpenShift Virtualization.pdf
クラウドネイティブなサーバー仮想化基盤 - OpenShift Virtualization.pdfクラウドネイティブなサーバー仮想化基盤 - OpenShift Virtualization.pdf
クラウドネイティブなサーバー仮想化基盤 - OpenShift Virtualization.pdfFumieNakayama
 
デジタル・フォレンジックの最新動向(2024年4月27日情洛会総会特別講演スライド)
デジタル・フォレンジックの最新動向(2024年4月27日情洛会総会特別講演スライド)デジタル・フォレンジックの最新動向(2024年4月27日情洛会総会特別講演スライド)
デジタル・フォレンジックの最新動向(2024年4月27日情洛会総会特別講演スライド)UEHARA, Tetsutaro
 
NewSQLの可用性構成パターン(OCHaCafe Season 8 #4 発表資料)
NewSQLの可用性構成パターン(OCHaCafe Season 8 #4 発表資料)NewSQLの可用性構成パターン(OCHaCafe Season 8 #4 発表資料)
NewSQLの可用性構成パターン(OCHaCafe Season 8 #4 発表資料)NTT DATA Technology & Innovation
 
CTO, VPoE, テックリードなどリーダーポジションに登用したくなるのはどんな人材か?
CTO, VPoE, テックリードなどリーダーポジションに登用したくなるのはどんな人材か?CTO, VPoE, テックリードなどリーダーポジションに登用したくなるのはどんな人材か?
CTO, VPoE, テックリードなどリーダーポジションに登用したくなるのはどんな人材か?akihisamiyanaga1
 

Dernier (8)

業務で生成AIを活用したい人のための生成AI入門講座(社外公開版:キンドリルジャパン社内勉強会:2024年4月発表)
業務で生成AIを活用したい人のための生成AI入門講座(社外公開版:キンドリルジャパン社内勉強会:2024年4月発表)業務で生成AIを活用したい人のための生成AI入門講座(社外公開版:キンドリルジャパン社内勉強会:2024年4月発表)
業務で生成AIを活用したい人のための生成AI入門講座(社外公開版:キンドリルジャパン社内勉強会:2024年4月発表)
 
自分史上一番早い2024振り返り〜コロナ後、仕事は通常ペースに戻ったか〜 by IoT fullstack engineer
自分史上一番早い2024振り返り〜コロナ後、仕事は通常ペースに戻ったか〜 by IoT fullstack engineer自分史上一番早い2024振り返り〜コロナ後、仕事は通常ペースに戻ったか〜 by IoT fullstack engineer
自分史上一番早い2024振り返り〜コロナ後、仕事は通常ペースに戻ったか〜 by IoT fullstack engineer
 
AWS の OpenShift サービス (ROSA) を使った OpenShift Virtualizationの始め方.pdf
AWS の OpenShift サービス (ROSA) を使った OpenShift Virtualizationの始め方.pdfAWS の OpenShift サービス (ROSA) を使った OpenShift Virtualizationの始め方.pdf
AWS の OpenShift サービス (ROSA) を使った OpenShift Virtualizationの始め方.pdf
 
モーダル間の変換後の一致性とジャンル表を用いた解釈可能性の考察 ~Text-to-MusicとText-To-ImageかつImage-to-Music...
モーダル間の変換後の一致性とジャンル表を用いた解釈可能性の考察  ~Text-to-MusicとText-To-ImageかつImage-to-Music...モーダル間の変換後の一致性とジャンル表を用いた解釈可能性の考察  ~Text-to-MusicとText-To-ImageかつImage-to-Music...
モーダル間の変換後の一致性とジャンル表を用いた解釈可能性の考察 ~Text-to-MusicとText-To-ImageかつImage-to-Music...
 
クラウドネイティブなサーバー仮想化基盤 - OpenShift Virtualization.pdf
クラウドネイティブなサーバー仮想化基盤 - OpenShift Virtualization.pdfクラウドネイティブなサーバー仮想化基盤 - OpenShift Virtualization.pdf
クラウドネイティブなサーバー仮想化基盤 - OpenShift Virtualization.pdf
 
デジタル・フォレンジックの最新動向(2024年4月27日情洛会総会特別講演スライド)
デジタル・フォレンジックの最新動向(2024年4月27日情洛会総会特別講演スライド)デジタル・フォレンジックの最新動向(2024年4月27日情洛会総会特別講演スライド)
デジタル・フォレンジックの最新動向(2024年4月27日情洛会総会特別講演スライド)
 
NewSQLの可用性構成パターン(OCHaCafe Season 8 #4 発表資料)
NewSQLの可用性構成パターン(OCHaCafe Season 8 #4 発表資料)NewSQLの可用性構成パターン(OCHaCafe Season 8 #4 発表資料)
NewSQLの可用性構成パターン(OCHaCafe Season 8 #4 発表資料)
 
CTO, VPoE, テックリードなどリーダーポジションに登用したくなるのはどんな人材か?
CTO, VPoE, テックリードなどリーダーポジションに登用したくなるのはどんな人材か?CTO, VPoE, テックリードなどリーダーポジションに登用したくなるのはどんな人材か?
CTO, VPoE, テックリードなどリーダーポジションに登用したくなるのはどんな人材か?
 

難しそうで難しくない少し難しいClojure並行処理

  • 1. 難しそうで 難しくない 少し難しい Clojure並行処理 角田直行 <kakuda@gmail.com>
  • 2. はじめに • 今回はClojureで用意されている スレッド処理、並行処理について • Clojureの基本文法が分かっている ことが前提 • 桃屋のやつはまだ食べた事ないス (辛いのが苦手な人でも大丈夫?)
  • 3. 背景 • 時代はマルチコア • 分散(Hadoop)なんて流行んない (嘘 • そんなにサーバもデータも無い • 1台で最大限のパフォーマンスを求める のが現実的
  • 5. マルチスレッド • はっきり言って難しい • 「The Art∼」は原理編で挫折中 • java.util.concurrent APIはスゴいけど まだまだ敷居が高い
  • 7. Clojure Concurrency • Immutable(不変)な設計 • Software Transactional Memory • プロセス間は対象外 • Java Concurrent APIも使える
  • 8. Immutable (def book {:title "1Q84" :author "村上春樹"}) (assoc book :publisher "新潮社") ; => {:publisher "新潮社", :title "1Q84", :author "村上春樹"} book ;=> {:title "1Q84", :author "村上春樹"}
  • 10. Var
  • 11. Var • mutable(可変) • defで値を束縛 • bindingマクロでThreadローカルな 値を束縛可能
  • 12. bindingマクロ (def v 1) (println v) ; -> 1 (binding [v 23] (println v)) ;-> 23 (do (binding [v 456] (println v)) (println v)) ; -> 456 ; 1
  • 13. Ref
  • 14. Ref • 基本は変更不可 • トランザクション内で値を変更可能 • STM • 自動で再実行 • 副作用に注意 (io!マクロで防御)
  • 15. Ref (def cd (ref {:title "Orchestrion" :artist "Pat Metheny"})) cd ; => #<Ref@26b496d: {:title "Orchestrion", :artist "Pat Metheny"}> @cd ; => {:title "Orchestrion", :artist "Pat Metheny"} 値を見るには@をつける (リーダマクロ)
  • 16. ref-set (ref-set cd {:price 2680 :genre "Jazz"}) ; => java.lang.IllegalStateException: No transaction running (NO_SOURCE_FILE:0) (dosync (ref-set cd {:price 2680 :genre "Jazz"})) ; => {:price 2680, :genre "Jazz"} @cd ; => {:price 2680, :genre "Jazz"} 別の情報をセットする際に使う
  • 17. alter (alter cd assoc :year 2010) ; => java.lang.IllegalStateException: No transaction running (NO_SOURCE_FILE:0) (dosync (alter cd assoc :year 2010)) ; => {:year 2010, :title "Orchestrion", :artist "Pat Metheny"} @cd ; => {:year 2010, :title "Orchestrion", :artist "Pat Metheny"} 前の情報を更新する際に使う
  • 18. commute (dosync (commute cd assoc :year 2010)) ; => {:year 2010, :title "Orchestrion", :artist "Pat Metheny"} @cd ; => {:year 2010, :title "Orchestrion", :artist "Pat Metheny"} alterと何が違う?
  • 19. alter or commute • 基本alterを使え • alterは、トランザクション中で 指定された順序で実行する • commuteは順序は気にしない • パフォーマンスはcommuteが上
  • 20. alter alter (def logs (ref "")) (with-new-thread (with-new-thread (dosync (dosync (println "--スレッドA 更新開始:" @logs) (println "****スレッドB 更新開始:" @logs) (alter logs str "A") (Thread/sleep 1000) (println "--スレッドA 更新完了:" @logs) (alter logs str "B") (Thread/sleep 4000) (println "****スレッドB 更新完了:" @logs) (println "--スレッドA 終了:" @logs))) (println "****スレッドB 終了:" @logs))) Aのトランザクション中 Bはリトライし続ける
  • 21. --スレッドA 更新開始: --スレッドA 更新完了: A Aのトランザクションは ****スレッドB 更新開始: 終わってないので空のまま ****スレッドB 更新開始: alterしようとしたら ****スレッドB 更新開始: Aが終わってないので ****スレッドB 更新開始: 再実行 --スレッドA 終了: A ****スレッドB 更新開始: A Aのトランザクションが ****スレッドB 更新完了: AB 終わって値が反映 ****スレッドB 終了: AB
  • 22. alter commute (def logs (ref "")) (with-new-thread (with-new-thread (dosync (dosync (println "--スレッドA 更新開始:" @logs) (println "****スレッドB 更新開始:" @logs) (alter logs str "A") (Thread/sleep 1000) (println "--スレッドA 更新完了:" @logs) (commute logs str "B") (Thread/sleep 4000) (println "****スレッドB 更新完了:" @logs) (println "--スレッドA 終了:" @logs))) (println "****スレッドB 終了:" @logs))) Aのトランザクション中でも Bは更新を行う
  • 23. --スレッドA 更新開始: --スレッドA 更新完了: A ****スレッドB 更新開始: commuteは更新順序を気 ****スレッドB 更新完了: B にしないので更新する ****スレッドB 終了: B ****スレッドB 更新開始: Aが終わってないので再実行 ****スレッドB 更新完了: B ****スレッドB 終了: B ****スレッドB 更新開始: ****スレッドB 更新完了: B ****スレッドB 終了: B ****スレッドB 更新開始: --スレッドA 終了: A Aのトランザクションが ****スレッドB 更新完了: AB 終わって値が反映 ****スレッドB 終了: AB
  • 24. commute alter (def logs (ref "")) (with-new-thread (with-new-thread (dosync (dosync (println "--スレッドA 更新開始:" @logs) (println "****スレッドB 更新開始:" @logs) (commute logs str "A") (Thread/sleep 1000) (println "--スレッドA 更新完了:" @logs) (alter logs str "B") (Thread/sleep 4000) (println "****スレッドB 更新完了:" @logs) (println "--スレッドA 終了:" @logs))) (println "****スレッドB 終了:" @logs))) スレッドBが commuteの場合も同様
  • 25. --スレッドA 更新開始: --スレッドA 更新完了: A ****スレッドB 更新開始: ****スレッドB 更新完了: B ****スレッドB 終了: B --スレッドA 終了: A @logs ;=> "BA"
  • 26. ensure (def logs (ref "")) (with-new-thread (with-new-thread (dosync (dosync (ensure logs) (println "--スレッドA 更新開始:" @logs) (println "****スレッドB 更新開始:" @logs) (alter logs str "A") (Thread/sleep 1000) (println "--スレッドA 更新完了:" @logs) (alter logs str "B") (Thread/sleep 4000) (println "****スレッドB 更新完了:" @logs) (println "--スレッドA 終了:" @logs))) (println "****スレッドB 終了:" @logs))) 変更から保護する
  • 27. ensure --スレッドA 更新開始: --スレッドA 更新完了: A Aが終わるまで --スレッドA 終了: A Bはensureにより待機 ****スレッドB 更新開始: A ****スレッドB 更新完了: AB ****スレッドB 終了: AB
  • 28. Atom
  • 29. Atom • Refに似ている • 独立した値を管理 • 他に依存する値がない前提なので トランザクションは必要としない • swap!は再実行される(副作用禁止!)
  • 30. Atom (def song (atom {:title "リハーサル" :artist "近藤夏子"})) song ;=> #<Atom@5e54777e: {:title "リハーサル", :artist "近藤夏子"}> @song ;=> {:title "リハーサル", :artist "近藤夏子"} Refと同様
  • 31. reset! (reset! song {:title "sad to say" :artist "Jasmine"}) ;=> {:title "sad to say", :artist "Jasmine"} @song ;=> {:title "sad to say", :artist "Jasmine"} トランザクションは利用しないので dosync等は要らない
  • 32. swap! @song ;=> {:title "リハーサル", :artist "近藤夏子"} (swap! song assoc :title "リアルでゴメン...") ;=> {:title "リアルでゴメン...", :artist "近藤夏子"} @song ;=> {:title "リアルでゴメン...", :artist "近藤夏子"} 内部で (AtomicReferenceの) Compare-And-Setを行っている
  • 33. Agent
  • 34. Agent • 個々で値を管理 • (Erlang/Scalaの)Actorっぽい • 分散(プロセス間通信)はしない • 変更は非同期に行われる • 結果 or エラー取得は問い合わせ必要
  • 35. Agent (def fetcher (agent “”)) fetcher ;=> #<Agent@39edd9b3: ""> @fetcher ;=> “” Refと同様
  • 36. send (use '[clojure.contrib.io :only (slurp*)]) (defn fetch-title [_ url] (Thread/sleep 2000) (second (re-find #"<title>(.*?)</title>" (slurp* url)))) (send fetcher fetch-title "http://bit.ly/") ;=> #<Agent@39edd9b3: ""> @fetcher ;=> "bit.ly, a simple url shortener" sendで渡す関数の第1引数はagentの値
  • 37. send (do (send fetcher fetch-title "http://bit.ly/") (println "send 直後: " @fetcher) send 直後: (Thread/sleep 1000) send 1秒後: (println "send 1秒後: " @fetcher) send 4秒後: bit.ly, a simple url shortener (Thread/sleep 3000) (println "send 4秒後: " @fetcher)) 直後、1秒後は関数の結果が まだ求まっていないため 更新されていない
  • 38. await, await-for (do (send fetcher fetch-title "http://bit.ly/") await 直後: bit.ly, a simple url shortener (await fetcher) (println "await 直後: " @fetcher)) (do (send fetcher fetch-title "http://bit.ly/") (await-for 1500 fetcher) await-for 直後: (println "await-for 直後: " @fetcher) await-for 3秒後: bit.ly, a simple url shortener (Thread/sleep 3000) (println "await-for 3秒後: " @fetcher))   await: 結果が返るまで待つ await-for: 最低タイムアウト(ms)待つ
  • 39. 連続実行 (time (do (send fetcher fetch-title "http://www.google.com/") (send fetcher fetch-title "http://www.apple.com/") (send fetcher fetch-title "http://www.yahoo.com/") (await fetcher) (println "終了: " @fetcher))) ; -> 終了: Yahoo! ; "Elapsed time: 7892.725 msecs"
  • 40. agent-errors (send fetcher fetch-title "http://notfound/") ;=> #<Agent@39edd9b3: ""> fetcher ;=> #<Agent@39edd9b3 FAILED: ""> (agent-errors fetcher) (#<UnknownHostException java.net.UnknownHostException: notfound>) (clear-agent-errors fetcher)でクリア
  • 41. おさらい Var Ref Atom Agent 可変 不変 不変 不変 同スレッド内 共有 共有 共有 同期 同期 同期 非同期 協調 協調 自律 協調
  • 42. まとめ • 基本は変更しない(Immutablityを保つ) • 変更する際は特性に応じて 4種の型(Var, Ref, Atom, Agent)から 選択する • 分からない場合はちょっと書いて試す
  • 43. 参照資料 • プログラミングClojure • Clojure 本家 • Clojure Concurrency (動画) • Software Transactional Memory
  • 44. ソース • clojure.lang.LockingTransaction → STMのトランザクション処理 • clojure.lang.Ref • clojure.lang.Agent • clojure.lang.Atom

Notes de l'éditeur

  1. \n
  2. \n
  3. \n
  4. \n
  5. \n
  6. \n
  7. \n
  8. \n
  9. \n
  10. \n
  11. \n
  12. \n
  13. \n
  14. \n
  15. \n
  16. \n
  17. \n
  18. \n
  19. \n
  20. \n
  21. \n
  22. \n
  23. \n
  24. \n
  25. \n
  26. \n
  27. \n
  28. \n
  29. \n
  30. \n
  31. \n
  32. \n
  33. \n
  34. \n
  35. \n
  36. \n
  37. \n
  38. \n
  39. \n
  40. \n
  41. \n
  42. \n
  43. \n
  44. \n
  45. \n
  46. \n