マスターオブゴールーチンアンドチャネル スタートGo #1

2,269 views

Published on

GDG名古屋で行なったスタートGo#1のスライドです。

Published in: Technology
1 Comment
11 Likes
Statistics
Notes
  • わかりやすーい!
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
No Downloads
Views
Total views
2,269
On SlideShare
0
From Embeds
0
Number of Embeds
13
Actions
Shares
0
Downloads
6
Comments
1
Likes
11
Embeds 0
No embeds

No notes for slide

マスターオブゴールーチンアンドチャネル スタートGo #1

  1. 1. マスター オブゴールーチンアンドチャネル2013/1/19(Sat)スタートGo #1@GDG名古屋
  2. 2. 自己紹介上田拓也豊橋技術科学大学大学院電子・情報工学専攻博士後期課程3年twitter : @tenntennblog: http://u.hinoichi.net
  3. 3. アジェンダ● 前回までのお話● マスター オブ ゴールーチン アンド チャネル● 何か作って練習しよう!
  4. 4. アジェンダ● 前回までのお話 ←今ココ ○ Go言語の特徴 ○ 基本的な文法 ○ Gopher君のかわいさ(重要)● マスター オブ ゴールーチン アンド チャネル● 何か作って練習しよう!
  5. 5. 前回までのお話
  6. 6. Go言語の特徴● シンプルな文法/設計 ○ 曖昧さを排除した設計 ○ 強い静的型付け ○ 型推論ができるので、型を省略できる ○ ポインタはあるがポインタ演算はなし ○ 複数の戻り値を返す関数 ○ ダックタイピング● ゴールーチンとチャネル● 豊富なライブラリ群 ○ goツールは最強 ○ Web系パッケージの豊富さ ○ Google App Engine for go
  7. 7. 基本的な文法 その1package main ← main関数のある  パッケージは必ずmainimport "fmt"func main() { fmt.Println("Hello, 世界")}
  8. 8. 基本的な文法 その1package mainimport "fmt" ← importで使いたいパッケー ジをインポートするfunc main() { fmt.Println("Hello, 世界")}
  9. 9. 基本的な文法 その1package mainimport "fmt"func main() { ← funcで関数を宣言 fmt.Println("Hello, 世界")}
  10. 10. 基本的な文法 その1package mainimport "fmt"func main() { fmt.Println("Hello, 世界")} ↑↑ 他のパッケージの関数などは パッケージ名を付けて呼び出 す
  11. 11. 基本的な文法 その1package mainimport "fmt"func main() { fmt.Println("Hello, 世界")} ↑↑ 行末に文の区切りを表すセミコロンは不 要 勝手にコンパイラが付ける(重要)
  12. 12. 基本的な文法 その2package mainimport "fmt"func main() { var s1 string = "Hello, " ← varで変数を宣言 s2 := "世界"  型は後ろに書く fmt.Println(s1 + s2)}
  13. 13. 基本的な文法 その2package mainimport "fmt"func main() { var s1 string = "Hello, " s2 := "世界" ← := で宣言&初期化   型は省略可 fmt.Println(s1 + s2)}
  14. 14. 基本的な文法 その3package mainimport "fmt"func isOdd(n int) bool { ← 戻り値は後ろに書く return n % 2 == 0  引数の型も後ろ}func main() { if isOdd(100) { fmt.Println("偶数") } else { fmt.Println("奇数") }}
  15. 15. 基本的な文法 その3package mainimport "fmt"func isOdd(n int) bool { return n % 2 == 0}func main() { if isOdd(100) { ← ifの()はいらない fmt.Println("偶数") } else { fmt.Println("奇数") }}
  16. 16. 基本的な文法 その4package mainimport "fmt"func fact(n int) int { m := 1 for i := 2; i <= n; i++ { ← forの()はいらない m *= i } return m}func main() { fmt.Println(fact(5))}
  17. 17. 基本的な文法 その5package mainimport "fmt"func main() { strs := [5]string{"A", "B", "C", "D", "E"} slice := strs[1:3] ↑ // 0 B 配列 // 1 C ポインタではなく値でやり取り for i, s := range slice { (重要) fmt.Println(i, s) }}
  18. 18. 基本的な文法 その5package main strs : "A" "B" "C" "D" "E"import "fmt"func main() { slice strs := [5]string{"A", "B", "C", "D", "E"} slice := strs[1:3] ←スライス // 0 B  配列を切り取ったもの // 1 C  値ではなく参照 for i, s := range slice { ※ 通常は配列は使わない fmt.Println(i, s) slice := []{1, 2, 3} }}
  19. 19. 基本的な文法 その5package mainimport "fmt"func main() { strs := [5]string{"A", "B", "C", "D", "E"} slice := strs[1:3] // 0 B // 1 C for i, s := range slice { ← rangeでfor each fmt.Println(i, s) }}
  20. 20. 基本的な文法 その6package mainimport "fmt"type Student struct { ← typeで型を宣言 no string type 名前 実体 name string}func main() { s := &Student{"041701", "Takuya Ueda"} fmt.Println(s)}
  21. 21. 基本的な文法 その6package mainimport "fmt"type Student struct { no string name string}func main() { s := &Student{"041701", "Takuya Ueda"} fmt.Println(s) ↑} 構造体の初期化 &でポインタ
  22. 22. 基本的な文法 その7package mainimport "fmt"type Hex int ← 組込み型のエイリアスfunc (h Hex) String() { return fmt.Sprintf("%x")}func main() { fmt.Println(Hex(100))}
  23. 23. 基本的な文法 その7package mainimport "fmt"type Hex intfunc (h Hex) String() string { ← メソッド  指定した型の値を return fmt.Sprintf("0x%x", int(h))}  レシーバとして  メソッドを作るfunc main() { fmt.Println(Hex(100))}
  24. 24. 基本的な文法 その7package mainimport "fmt"type Hex intfunc (h Hex) String() string { return fmt.Sprintf("0x%x", int(h))} ↑func main() { 型(値)でキャスト fmt.Println(Hex(100))}
  25. 25. 基本的な文法 その7 fmtパッケージにて宣言package main type interface Stringer {import "fmt" String() stringtype Hex int }func (h Hex) String() string { 実装 return fmt.Sprintf("0x%x", int(h))}func main() { fmt.Println(Hex(100))} ↑ fmt.Stringerとして振る舞う (ダックタイピング) 0x64と出力される
  26. 26. 問題を解いてみよう! ※ここまで1時間の予定
  27. 27. その前に、単体テストのやり方!// hoge.gopackage hogefunc FooBar() string { ← テスト対象 return "foobar"}// hoge_test.go ← xxx_test.goはテストコードpackage hogeimport "testing"func TestHoge(t *testing.T) { if FooBar() != "foobar" { t.Errorf("foobarと返す必要があります") } ↑} TestXxx(t *testing.T)はテストメソッド
  28. 28. その前に、単体テストのやり方!● goツールのtestで単体テストが実行できる ○ $ go test パッケージ名 ■ パッケージ名を指定する ○ $ go test ■ カレントディレクトリのテストを実行● 1つのgoファイル毎に1つのテストファイルを作るのが Go言語流
  29. 29. 問題1偶数個の要素を持つ、任意の文字列のスライスからランダムにペアを作るプログラムを作成せよ。なお、テストコードが通るように作ること。
  30. 30. Gopher君を拝んで休憩http://golang.org http://dancallahan.info/journal/go-overview/ Gopher Gopher (マスコット) (Go言語ユーザ)
  31. 31. アジェンダ● 前回までのお話● マスター オブ ゴールーチン アンド チャネル ← 今ココ ○ ゴールーチン ○ チャネル● 何か作って練習しよう!
  32. 32. マスター・オブゴールーチン アンド チャネル やっと本題です!
  33. 33. Concurrency is not Parallelism● ConcurrencyとParallelismは違う by Rob Pike ○ Concurrency=並行? ○ Parallelism=並列? ■ たぶん● Concurrency ○ 同時にいくつかの事を扱う事● Parallelism ○ 同時にいくつかの計算を行なう事
  34. 34. Concurrency is... by Rob PikeRob Pike氏のプレゼンのまとめでは、 ○ Concurrency is powerful. ○ Concurrency is not parallelism. ○ Concurrency enables parallelism. ○ Concurrency makes parallelism (and scaling and everything else) easy.とある。
  35. 35. ゴールーチンとは?● Go言語ではゴールーチンを用いてConcurrencyを実 現している● スレッドに似ている... ○ しかし、もっとCheap(=コストが低い) ○ LinuxやUnixのスレッドとは違う ■ スレッドの上にいくつも乗っている
  36. 36. ゴールーチンを使うには?● 「go 関数呼び出し」【例】 func main() { go f() // 終了を待たない g() // すぐにここに来る }
  37. 37. go + クロージャpackage mainimport "fmt"func main() { go func() { fmt.Println("別のゴールーチン") }() fmt.Println("mainゴールーチン")}
  38. 38. 並列度● デフォルトでは並列度は1になっている ○ runtime.NumCPU()で利用可能なコア数が取得 できる ○ runtime.GOMAXPROCS()で並列度を設定する 【例】 runtime.GOMAXPROCS(runtime.NumCPU())● 指定した並列度でゴールーチンを動かす ○ あまり増やしても意味がない
  39. 39. ゴールーチン間のデータのやりとり● 共有の変数を使う? func main() { done := false go func() { time.Sleep(time.Second * 3) done = true }() for !done { time.Sleep(time.Millisecond) } }
  40. 40. これは大丈夫?// これはダメ!! // 続きpackage main for i := 1; i <= 10; i++ {import "fmt" fmt.Println(n, "+", i)import "time" n += 1func main() { time.Sleep(500) n := 1 } go func() { } for i := 2; i <= 5; i++ { fmt.Println(n, "*", i) n *= i } だめ!! }()// 続く
  41. 41. 何が問題なのか?どう解決するの?【問題】● 同時に複数のゴールーチンからアクセス● 競合が起きる【解決方法】● 1つのゴールーチンからのみアクセスする● チャネルを使う!
  42. 42. チャネルとは?● ゴールーチン間でデータをやりとりする経路?のよう なもの ○ CSPが元になっている ■ C. A. R. Hoare: Communicating Sequential Processes (CACM 1978)
  43. 43. チャネルとは?● ゴールーチン間でデータをやりとりするパイプ?のよ うなものゴールーチン1 "hoge" ゴールーチン2 チャネル
  44. 44. チャネルとは?● チャネルへデータを送ったり、チャネルからデータを 受け取ったりできるゴールーチンは同時に1つのみ (排他制御)ゴールーチン1 "hoge" ゴールーチン2 チャネル
  45. 45. チャネルの宣言方法と使い方● 宣言 ○ ch := make(chan 型) // 容量0 ○ ch := make(chan 型, 容量)● 送信 ○ ch <- 100 ○ ch <- "hoge" ■ 容量いっぱいであれば取り出されるの待機する ■ 容量0の場合は常に待つ● 受信 ○ n := <- ch ■ 送られてくるまで待機する
  46. 46. チャネルの宣言方法と使い方func main() { done := make(chan bool) // 容量0 go func() { time.Sleep(time.Second * 3) done <- true }() <-done // 待つ fmt.Println("done")}
  47. 47. どう使って行くのか?● 沢山の独立したゴールーチンがチャネルを使っ てやり取りをする! ○ Concurrencyになる● ゴールーチンのコストは安いので、沢山作って もそんなに問題ない ○ ※コストが0ではない
  48. 48. ファーストクラスオブジェクト● チャネルはファーストクラスオブジェクト ○ 変数に入れれる ○ 引数に渡せる ○ 戻り値に取れる● 戻り値として、チャネルを返す関数は多用される ○ timeパッケージ ○ 5分間待つ <-time.After(5 * time.Minute)
  49. 49. ReadOnlyとWriteOnly● 通常のチャネルは双方向 ○ 引数や戻り値で渡す場合は困る func hoge(in chan int) { n := <-in // 入力として使うのが正解! in <- 100 // 間違った使い方ができる! }
  50. 50. ReadOnlyとWriteOnly● ReadOnly ○ in := make(<-chan int)● WriteOnly ○ out := make(chan<- int)● キャスト ○ 双方向チャネルはキャストでReadOnlyまたは WriteOnlyに変化できる ○ ch := make(chan int) ○ in := (<-chan int)(ch) ○ out := (chan<- int)(ch)
  51. 51. range● for文のrangeで送られてくるデータを次々に取 り出せる // 1分ごとに現在時間を出す for now := range time.Tick(1 * time.Minute) { fmt.Println(now) }
  52. 52. select● selectで同時に複数のチャネルからの受信を待 つことができる // タイムアウトの処理 select { case m := <-ch: // mを使った処理 case <-time.After(5 * time.Minute): fmt.Println("タイムアウト") }
  53. 53. 問題2フィボナッチ数列を生成する関数を作成せよ。なお、テストコードが通るように。
  54. 54. 問題30〜nまでの数列を生成するプログラムを作成せよ。また、上記のプログラムで作成した数列をstep分だけ飛ばすプログラムを作成せよ。なお、テストコードが通るように作成せよ。

×