SlideShare a Scribd company logo
難しそうで
  難しくない
  少し難しい
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

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

More Related Content

PPTX
serverspecを使用したサーバ設定テストの実例
PDF
Tottoruby 20110903
PPT
CPANの依存モジュールをもう少し正しく検出したい
PDF
Redunduncy of NAT instance on AWS/VPC
PDF
Java8 lambdas chap03
PPTX
CMSとPerlで遊ぼう
PDF
"Programming Hive" Reading #1
PDF
Miyazaki.js vol.1 スコープの話
serverspecを使用したサーバ設定テストの実例
Tottoruby 20110903
CPANの依存モジュールをもう少し正しく検出したい
Redunduncy of NAT instance on AWS/VPC
Java8 lambdas chap03
CMSとPerlで遊ぼう
"Programming Hive" Reading #1
Miyazaki.js vol.1 スコープの話

What's hot (20)

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

Viewers also liked (20)

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

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

KEY
Perl 非同期プログラミング
PDF
Clojure
PDF
Em synchrony について
PDF
197x 20090704 Scalaで並行プログラミング
PDF
Project Loom - 限定継続と軽量スレッド -
PDF
GHC 6.12.1 マルチコア対応ランタイムシステムについて
KEY
Actor&stm
PDF
大江戸Ruby会議01 "mission critical"なシステムでも使えるThreadの作り方
PDF
Error handling in Erlang and Scala
PDF
Node.js入門
PDF
Rubyの御先祖CLUのお話(原本)
PDF
Clojure
PDF
今日からはじめるGPars
PDF
ICT ERA + ABC 2012 Tohoku
PPTX
イベント駆動プログラミングとI/O多重化
PDF
できる!並列・並行プログラミング
PPT
"Formalizing Architectural Connection" 紹介
PDF
Erlangやってみた
PPTX
冬のLock free祭り safe
PDF
Pythonによる非同期プログラミング入門
Perl 非同期プログラミング
Clojure
Em synchrony について
197x 20090704 Scalaで並行プログラミング
Project Loom - 限定継続と軽量スレッド -
GHC 6.12.1 マルチコア対応ランタイムシステムについて
Actor&stm
大江戸Ruby会議01 "mission critical"なシステムでも使えるThreadの作り方
Error handling in Erlang and Scala
Node.js入門
Rubyの御先祖CLUのお話(原本)
Clojure
今日からはじめるGPars
ICT ERA + ABC 2012 Tohoku
イベント駆動プログラミングとI/O多重化
できる!並列・並行プログラミング
"Formalizing Architectural Connection" 紹介
Erlangやってみた
冬のLock free祭り safe
Pythonによる非同期プログラミング入門

難しそうで難しくない少し難しい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

Editor's Notes