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

7,494 views
7,226 views

Published on

Practical guide of clojure concurrency(var, ref, atom, agent)

Published in: Technology
0 Comments
26 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
7,494
On SlideShare
0
From Embeds
0
Number of Embeds
207
Actions
Shares
0
Downloads
48
Comments
0
Likes
26
Embeds 0
No embeds

No notes for slide
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • 難しそうで難しくない少し難しいClojure並行処理

    1. 1. 難しそうで 難しくない 少し難しいClojure並行処理角田直行 <kakuda@gmail.com>
    2. 2. はじめに• 今回はClojureで用意されている スレッド処理、並行処理について• Clojureの基本文法が分かっている ことが前提• 桃屋のやつはまだ食べた事ないス (辛いのが苦手な人でも大丈夫?)
    3. 3. 背景• 時代はマルチコア• 分散(Hadoop)なんて流行んない (嘘 • そんなにサーバもデータも無い• 1台で最大限のパフォーマンスを求める のが現実的
    4. 4. マルチスレッド
    5. 5. マルチスレッド• はっきり言って難しい • 「The Art∼」は原理編で挫折中• java.util.concurrent APIはスゴいけど まだまだ敷居が高い
    6. 6. そこでClojure
    7. 7. Clojure Concurrency• Immutable(不変)な設計• Software Transactional Memory• プロセス間は対象外• Java Concurrent APIも使える
    8. 8. Immutable(def book {:title "1Q84" :author "村上春樹"})(assoc book :publisher "新潮社"); => {:publisher "新潮社", :title "1Q84", :author "村上春樹"}book;=> {:title "1Q84", :author "村上春樹"}
    9. 9. 4つの型• Var• Ref• Atom• Agent
    10. 10. Var
    11. 11. Var• mutable(可変)• defで値を束縛• bindingマクロでThreadローカルな 値を束縛可能
    12. 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. 13. Ref
    14. 14. Ref• 基本は変更不可• トランザクション内で値を変更可能 • STM • 自動で再実行 • 副作用に注意 (io!マクロで防御)
    15. 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. 16. ref-set(ref-set cd {:price 2680 :genre "Jazz"}); => java.lang.IllegalStateException: No transactionrunning (NO_SOURCE_FILE:0)(dosync (ref-set cd {:price 2680 :genre "Jazz"})); => {:price 2680, :genre "Jazz"}@cd; => {:price 2680, :genre "Jazz"} 別の情報をセットする際に使う
    17. 17. alter(alter cd assoc :year 2010); => java.lang.IllegalStateException: No transactionrunning (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. 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. 19. alter or commute• 基本alterを使え• alterは、トランザクション中で 指定された順序で実行する • commuteは順序は気にしない• パフォーマンスはcommuteが上
    20. 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. 21. --スレッドA 更新開始:--スレッドA 更新完了: A Aのトランザクションは****スレッドB 更新開始: 終わってないので空のまま****スレッドB 更新開始: alterしようとしたら****スレッドB 更新開始: Aが終わってないので****スレッドB 更新開始: 再実行--スレッドA 終了: A****スレッドB 更新開始: A Aのトランザクションが****スレッドB 更新完了: AB 終わって値が反映****スレッドB 終了: AB
    22. 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. 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. 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. 25. --スレッドA 更新開始:--スレッドA 更新完了: A****スレッドB 更新開始:****スレッドB 更新完了: B****スレッドB 終了: B--スレッドA 終了: A@logs;=> "BA"
    26. 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. 27. ensure--スレッドA 更新開始:--スレッドA 更新完了: A Aが終わるまで--スレッドA 終了: A Bはensureにより待機****スレッドB 更新開始: A****スレッドB 更新完了: AB****スレッドB 終了: AB
    28. 28. Atom
    29. 29. Atom• Refに似ている• 独立した値を管理 • 他に依存する値がない前提なので トランザクションは必要としない• swap!は再実行される(副作用禁止!)
    30. 30. Atom(def song (atom {:title "リハーサル" :artist "近藤夏子"}))song;=> #<Atom@5e54777e: {:title "リハーサル", :artist "近藤夏子"}>@song;=> {:title "リハーサル", :artist "近藤夏子"} Refと同様
    31. 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. 32. swap!@song;=> {:title "リハーサル", :artist "近藤夏子"}(swap! song assoc :title "リアルでゴメン...");=> {:title "リアルでゴメン...", :artist "近藤夏子"}@song;=> {:title "リアルでゴメン...", :artist "近藤夏子"} 内部で (AtomicReferenceの) Compare-And-Setを行っている
    33. 33. Agent
    34. 34. Agent• 個々で値を管理• (Erlang/Scalaの)Actorっぽい • 分散(プロセス間通信)はしない• 変更は非同期に行われる • 結果 or エラー取得は問い合わせ必要
    35. 35. Agent(def fetcher (agent “”))fetcher;=> #<Agent@39edd9b3: "">@fetcher;=> “” Refと同様
    36. 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. 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. 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. 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. 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. 41. おさらい Var Ref Atom Agent 可変 不変 不変 不変同スレッド内 共有 共有 共有 同期 同期 同期 非同期 協調 協調 自律 協調
    42. 42. まとめ• 基本は変更しない(Immutablityを保つ)• 変更する際は特性に応じて 4種の型(Var, Ref, Atom, Agent)から 選択する• 分からない場合はちょっと書いて試す
    43. 43. 参照資料• プログラミングClojure• Clojure 本家• Clojure Concurrency (動画)• Software Transactional Memory
    44. 44. ソース• clojure.lang.LockingTransaction → STMのトランザクション処理• clojure.lang.Ref• clojure.lang.Agent• clojure.lang.Atom
    45. 45. Q&Aといっても自分もまだ知らないことだらけ
    46. 46. ご清聴ありがとう ございました!

    ×