かずきのBlog@hatena

すきな言語は C# + XAML の組み合わせ。Azure Functions も好き。最近は Go 言語勉強中。日本マイクロソフトで働いていますが、ここに書いていることは個人的なメモなので会社の公式見解ではありません。

Go 言語勉強ログ その 3

blog.okazuki.jp

さて、引き続きやっていきます。A Tour of Go の Methods and interfaces からやっていきます。

メソッド

普通のオブジェクト指向言語にあるようなメソッドみたいに構造体やクラス内にメソッドを定義するというよりはレシーバーという特別な第一引数の値を受け取るような感じで関数を定義するとメソッドになるみたいです。 C# でいう拡張メソッドのイメージに近いなと感じました。あとは、C++ 言語の内部実装でメソッドの第一引数はクラスへのポインターだとかいうのを昔見た記憶があるので、それに近いなぁと思いました。

定義方法は func キーワードと関数名の間に括弧でくくってレシーバーを指定します。

package main

import "fmt"

type Person struct {
    Name string
}

func (p Person) SelfIntroduction() {
    fmt.Printf("%v です!", p.Name)
}

func main() {
    p := Person{"Tanaka"}
    p.SelfIntroduction() // Tanaka です!
}

特に問題なし。

レシーバー関数は同じパッケージ内の型になら定義できる。 別パッケージの型に対して関数生やしてやるみたいな C# の拡張メソッドみたいなことは出来ないみたいです。因みに、構造体じゃなくて以下のようなものも OK。

package main

import "fmt"

type Person string

func (p Person) SelfIntroduction() {
    fmt.Printf("%v です!", p)
}

func main() {
    p := Person("Tanaka")
    p.SelfIntroduction() // Tanaka です!
}

ポインターに対してレシーバーを定義できる。この場合はレシーバーの中身を書き換えることが出来る。

package main

import "fmt"

type Person struct {
    Name string
}

func (p Person) SelfIntroduction() {
    fmt.Printf("%v です!", p.Name)
}

func (p *Person) UpdateName(name string) {
    p.Name = name
}

func main() {
    p := Person{"Tanaka"}
    p.SelfIntroduction() // Tanaka です!
    p.UpdateName("Kimura")
    p.SelfIntroduction() // Kimura です!
}

ポインターレシーバーの方がコピーではなくポインター渡しなので大きな構造体の場合などで効率がいいみたい。

interface

Go のインターフェースは他の言語と違って実装されない。 インターフェースに定義されてるものを持ってる型は、そのインターフェースに代入出来るみたい。

package main

import "fmt"

type Greeter interface {
    Greet()
}

type Person struct {
    Name string
}

func (p Person) Greet() {
    fmt.Printf("%v です!\n", p.Name)
}

func SelfIntrocuction(greeter Greeter) {
    fmt.Println("自己紹介をしてもらいます!")
    greeter.Greet()
    fmt.Println("ありがとうございました!")
}

func main() {
    p := Person{"Tanaka"}
    SelfIntrocuction(p)
}

Greeter インターフェースと Person 構造体の間には何の継承関係も実装したとかいうのも書いてないですが Greeter を引数に受け取る SelfIntroduction 関数に渡せます。

さらに面白いのがインターフェースが nil になるケース。純粋に nil の場合は…

package main

import "fmt"

type Greeter interface {
    Greet()
}

func main() {
    var g Greeter // nil
    g.Greet()     // panic
}

実行するとこうなる。

panic: runtime error: invalid memory address or nil pointer dereference

思った通り。でも以下のようにすると…

package main

import "fmt"

type Greeter interface {
    Greet()
}

type Person struct {
    Name string
}

func (p *Person) Greet() {
    fmt.Println("Person#Greet called")
}

func main() {
    var p *Person     // nil
    var g Greeter = p // nil
    g.Greet()         // ok!!
}

まじかぁ…。これはインターフェースが実際には値を型を持っているからみたいです。最初にエラーになったのは本当に nil でエラーにならなかったほうは nil に見せかけて実は値が nil で型に *Person という情報を持ってるものなので、メソッド呼び出しても panic にはならない。

Person の Greet 関数で p にアクセスするとそこで panic になる。

package main

import "fmt"

type Greeter interface {
    Greet()
}

type Person struct {
    Name string
}

func (p *Person) Greet() {
    fmt.Println(p.Name) // panic
}

func main() {
    var p *Person
    var g Greeter = p
    g.Greet()
}

一般的にはレシーバーに nil が渡って来ても動くことが望ましい場合は動くように作るみたいですね。

package main

import "fmt"

type Greeter interface {
    Greet()
}

type Person struct {
    Name string
}

func (p *Person) Greet() {
    if p == nil {
        fmt.Println("nil!!")
        return
    }
    fmt.Println(p.Name)
}

func main() {
    var p *Person
    var g Greeter = p
    g.Greet()
}

もう 1 つ面白い挙動としてレシーバーがポインターの場合とポインターじゃないものが混在してるケース。

これはコンパイルエラー

package main

import "fmt"

type Greeter interface {
    Greet()
    Detail()
}

type Person struct {
    Name string
}

// Greet はレシーバーがポインター
func (p *Person) Greet() {
    if p == nil {
        fmt.Println("nil!!")
        return
    }
    fmt.Println(p.Name)
}

// Detail はレシーバーがポインターじゃない
func (p Person) Detail() {
    fmt.Println(p.Name, " detail")
}

func main() {
    var g Greeter = Person{"Name"}
    g.Greet()
    g.Detail()
}

main の中で Person{"Name"} を &Person{"Name"} にすると OK。

package main

import "fmt"

type Greeter interface {
    Greet()
    Detail()
}

type Person struct {
    Name string
}

// Greet はレシーバーがポインター
func (p *Person) Greet() {
    if p == nil {
        fmt.Println("nil!!")
        return
    }
    fmt.Println(p.Name)
}

// Detail はレシーバーがポインターじゃない
func (p Person) Detail() {
    fmt.Println(p.Name, " detail")
}

func main() {
    var g Greeter = &Person{"Name"}
    g.Greet()
    g.Detail()
}

Greet を実装してるのはポインターのほうであってポインターじゃない奴はインターフェース実装してないじゃん?ってことでエラーになるみたいです。ポインターを渡しておけば、どっちでもいいみたい。

そして、空のインターフェースというのがある。これは Java や C# における object 型みたいに使えるみたい。何のメソッドも持たないインターフェースには何でも代入できるといった代物。interface{} と書く。

package main

import "fmt"

type Person struct {
    Name string
}

func (p *Person) Greet() {
    if p == nil {
        fmt.Println("nil!!")
        return
    }
    fmt.Println(p.Name)
}

func main() {
    var i interface{} = Person{"Tanaka"}
    fmt.Printf("%v, %T\n", i, i)
}

実行するとこんな感じ

{Tanaka}, main.Person

他の言語でいうダウンキャストは、タイプアサーションっていうみたい。

.(型名) でタイプアサーションが出来て、戻り値は 2 つでダウンキャストした結果と、成否が返ってくる。2つ目の戻り値を受け取らないでタイプアサーションに失敗すると panic になる。

package main

import "fmt"

type Person struct {
    Name string
}

func (p *Person) Greet() {
    if p == nil {
        fmt.Println("nil!!")
        return
    }
    fmt.Println(p.Name)
}

func main() {
    var i interface{} = Person{"Tanaka"}

    if p, ok := i.(Person); ok {
        p.Greet()
    } else {
        fmt.Println("failed")
    }
}

これは OK。実行すると Tanaka と表示されます。i に別の型が入っていると failed と表示されます。こんな感じ。

package main

import "fmt"

type Person struct {
    Name string
}

func (p *Person) Greet() {
    if p == nil {
        fmt.Println("nil!!")
        return
    }
    fmt.Println(p.Name)
}

type Dummy struct{}

func (d *Dummy) Greet() {}

func main() {
    var i interface{} = Dummy{}

    if p, ok := i.(Person); ok {
        p.Greet()
    } else {
        fmt.Println("failed")
    }
}

ok を受け取らないようにすると panic になる。こんな感じ

package main

import "fmt"

type Person struct {
    Name string
}

func (p *Person) Greet() {
    if p == nil {
        fmt.Println("nil!!")
        return
    }
    fmt.Println(p.Name)
}

type Dummy struct{}

func (d *Dummy) Greet() {}

func main() {
    var i interface{} = Dummy{}

    p := i.(Person) // panic
    p.Greet()
}

最近の言語は結構な割合で持ってる型スイッチを Go も持ってる。.(type) っていうのを switch に渡す。

package main

import "fmt"

type Person struct {
    Name string
}

func (p *Person) Greet() {
    if p == nil {
        fmt.Println("nil!!")
        return
    }
    fmt.Println(p.Name)
}

type Dummy struct{}

func (d *Dummy) Greet() {}

func NotTypeSwitchFunction(i interface{}) {
    if t, ok := i.(Person); ok {
        t.Greet()
        return
    }

    if _, ok := i.(Dummy); ok {
        fmt.Println("Dummy!!")
        return
    }

    fmt.Printf("知らない型 %v, %T\n", i, i)
}

func TypeSwitchFunction(i interface{}) {
    switch t := i.(type) {
    case Person:
        t.Greet()
    case Dummy:
        fmt.Println("Dummy!!")
    default:
        fmt.Printf("知らない型 %v, %T\n", t, t)
    }
}

func main() {
    // Person
    NotTypeSwitchFunction(Person{"Tanaka"})
    TypeSwitchFunction(Person{"Hoge"})
    // int
    NotTypeSwitchFunction(10)
    TypeSwitchFunction(100)
}

実行するとこんな感じ。

Tanaka
Hoge
知らない型 10, int
知らない型 100, int

ToString?

他の言語でオブジェクトの文字列表現を返す ToString/toString というメソッドですが、Go では fmt パッケージの Stringers インターフェースがそれにあたるみたいです。String メソッドがあるだけのインターフェース。

ということで、文字列として扱いたい場合には String メソッドを持ってれば OK。ここでもポインターに String メソッドを定義したら悲しいことが起きるケースがあります。

package main

import "fmt"

type Person struct {
    Name string
}

func (p *Person) String() string {
    if p == nil {
        return "<nil>"
    }
    return p.Name
}

func main() {
    p := Person{"Tanaka"}
    fmt.Println(p)  // {Tanaka}
    fmt.Println(&p) // Tanaka
}

まぁ、こういうことですよね。

package main

import "fmt"

type Person struct {
    Name string
}

func (p *Person) String() string {
    if p == nil {
        return "<nil>"
    }
    return p.Name
}

func main() {
    p := Person{"Tanaka"}
    i := interface{}(p)
    s, ok := i.(fmt.Stringer)
    fmt.Printf("ok = %v, s = %v\n", ok, s) // ok = false, s = <nil>

    i = interface{}(&p)
    s, ok = i.(fmt.Stringer)
    fmt.Printf("ok = %v, s = %v\n", ok, s) // ok = true, s = Tanaka
}

ここらへん Go のはまりどころになりそうな気がするけどどうだろう。因みにポインターとポインターじゃないものを受け取るメソッドを両方定義するとエラーになるので注意。どっちかに寄せるのがよさそう。

type Person struct {
    Name string
}

func (p *Person) String() string {
    if p == nil {
        return "<nil>"
    }
    return p.Name
}

func (p Person) String() string { // error!!
    if p == nil {
        return "<nil>"
    }
    return p.Name
}

エラー処理

最初にびっくりしたことの 1 つとして例外という仕組みが Go 言語にはないです。

えっ!?じゃぁどうするの!?というのですが戻り値で返すというのが方針みたいです。

C# でも例外は、もう取り返しがつかないエラーに対して使って、それ以外は戻り値などを使うべしというガイドラインがあるので、それなら例外いらんくない?って感じみたいですね。まだやってないので詳しくは知らないですが、たびたび panic というのが出てくるので、これが致命的に取り返しのつかないエラーに該当するものっぽい。

エラーを表すインターフェースが定義されてる。こんな感じで

type error interface {
    Error() string
}

つまり、これを実装してればエラー。そして Go では戻り値の 2 つ目にエラーの有無を返すのが一般的みたい。つまりこんな感じ。

package main

import "fmt"

type DivideZeroError struct {
    // 何かエラーについて有益な情報を持たせるときはここに定義
}

// error#Error() の実装
func (e *DivideZeroError) Error() string {
    return "0 で割らないで"
}

func div(x, y int) (int, error) {
    if y == 0 {
        return 0, &DivideZeroError{}
    }

    return x / y, nil
}

func main() {
    // OK case
    if answer, e := div(10, 3); e == nil {
        fmt.Printf("正常: %v\n", answer)
    } else {
        fmt.Printf(e.Error())
    }

    // Error case
    if answer, e := div(10, 0); e == nil {
        fmt.Printf("正常: %v\n", answer)
    } else {
        fmt.Printf(e.Error())
    }
}

実行するとこんな感じ

正常: 3
0 で割らないで

複数のタイプのエラーを返すような場合は型スイッチが便利そうかな?こんな感じで。

package main

import "fmt"

type DivideZeroError struct {
}

type LargeNumberError struct {
    Detail string
}

// error#Error() の実装
func (e *DivideZeroError) Error() string {
    return "0 で割らないで"
}

func (e *LargeNumberError) Error() string {
    return "数字が大きすぎ"
}

func div(x, y int) (int, error) {
    if y == 0 {
        return 0, &DivideZeroError{}
    }

    if x >= 100 || y >= 100 {
        return 0, &LargeNumberError{"100 以上はちょっと…"}
    }

    return x / y, nil
}

func main() {
    // OK case
    switch answer, e := div(10, 3); t := e.(type) {
    case nil:
        fmt.Printf("OK: %v\n", answer)
    case *DivideZeroError:
        fmt.Println(t.Error())
    case *LargeNumberError:
        fmt.Println(t.Error(), t.Detail)
    }
    // LargeNumberError case
    switch answer, e := div(100, 3); t := e.(type) {
    case nil:
        fmt.Printf("OK: %v\n", answer)
    case *DivideZeroError:
        fmt.Println(t.Error())
    case *LargeNumberError:
        fmt.Println(t.Error(), t.Detail)
    }
    // DivideZeroError case
    switch answer, e := div(10, 0); t := e.(type) {
    case nil:
        fmt.Printf("OK: %v\n", answer)
    case *DivideZeroError:
        fmt.Println(t.Error())
    case *LargeNumberError:
        fmt.Println(t.Error(), t.Detail)
    }
}

書いてて思ったけど戻り値で一旦受けて if で nil チェックしてエラーがあったら型スイッチのほうが自然に感じる。

package main

import "fmt"

type DivideZeroError struct {
}

type LargeNumberError struct {
    Detail string
}

// error#Error() の実装
func (e *DivideZeroError) Error() string {
    return "0 で割らないで"
}

func (e *LargeNumberError) Error() string {
    return "数字が大きすぎ"
}

func div(x, y int) (int, error) {
    if y == 0 {
        return 0, &DivideZeroError{}
    }

    if x >= 100 || y >= 100 {
        return 0, &LargeNumberError{"100 以上はちょっと…"}
    }

    return x / y, nil
}

func main() {
    // OK case
    answer, e := div(10, 3)
    if e == nil {
        // correct
        fmt.Printf("OK: %v\n", answer)
    } else {
        switch t := e.(type) {
        case *DivideZeroError:
            fmt.Println(t.Error())
        case *LargeNumberError:
            fmt.Println(t.Error(), t.Detail)
        default:
            fmt.Printf("Unknown error: %v, %T", t, t)
        }
    }
}

入出力

io パッケージを使う。io.Reader インターフェースがあっていろんなところで使われてる。Java や C# でいうところの Stream に通じるものがありそう。

io.Reader インターフェースは Read メソッドを提供してる。

// b に読み込んだデータが入る、n に読み込んだバイト数、err にエラー。終端に来たら io.EOF が err に入る
fnc (T) Read(b []byte) (n int, err error)

つまり最後まで読み込むときは io.EOF になるまで無限ループすればいい。これは A Tour of Go で示されてるコードをそのまま写経

package main

import (
    "fmt"
    "io"
    "strings"
)

func main() {
    r := strings.NewReader("Hello world")
    buffer := make([]byte, 8)
    for {
        n, err := r.Read(buffer)
        fmt.Printf("n = %v, err = %v, buffer = %v\n", n, err, buffer) // そのま情報を出力
        fmt.Printf("%q\n", buffer[:n])                                // 読み取ったデータだけ出力
        if err == io.EOF {
            break
        }
    }
}

結果はこんな感じ

n = 8, err = <nil>, buffer = [72 101 108 108 111 32 119 111]
"Hello wo"
n = 3, err = <nil>, buffer = [114 108 100 108 111 32 119 111]
"rld"
n = 0, err = EOF, buffer = [114 108 100 108 111 32 119 111]
""

スライスこんな感じに使うんだって思った。あと %q っていうのもあるんだ。

画像

唐突に画像を扱う方法が紹介されてた。image パッケージにある Image インターフェースを使う。

package image

type Image interface {
    ColorModel() color.Model
    Bounds() Rectangle
    At(x, y int) color.Color
}

これさえ実装すれば画像として振る舞えるってお手軽そう。ここも A Tour of Go のコードを写経しとく。

package main

import (
    "fmt"
    "image"
)

func main() {
    m := image.NewRGBA(image.Rect(0, 0, 100, 100))
    fmt.Println(m.Bounds())
    fmt.Println(m.At(0, 0).RGBA())
}

実行するとこんな感じ

(0,0)-(100,100)
0 0 0 0

RGBA 関数は r, g, b, a uint32 を返す。なので、これも実質同じ。

package main

import (
    "fmt"
    "image"
)

func main() {
    m := image.NewRGBA(image.Rect(0, 0, 100, 100))
    fmt.Println(m.Bounds())
    r, g, b, a := m.At(0, 0).RGBA()
    fmt.Println(r, g, b, a)
}

Goroutines

続けて Concurrency を見ていく。

ゴルーチンというらしい。軽量なスレッド。go 関数呼び出し で裏側で処理を動かせる。

package main

import (
    "fmt"
    "time"
)

func HeavyProcess(s string) {
    fmt.Printf("before: %v\n", s)
    time.Sleep(1000)
    fmt.Printf("after: %v\n", s)
}

func main() {
    // 裏で動く
    go HeavyProcess("first")
    go HeavyProcess("second")

    // ここで動く
    HeavyProcess("main")
    time.Sleep(2500)
}

実行するとこうなった。きっと実行のたびに違う結果になる。

before: second
before: main
before: first
after: main
after: second
after: first

裏側と呼び出し側での値の送受信はチャネルというのを使う。chan 型名 でチャネルになる。make を使って作ってチャネルオペレーターの <- を使ってチャネルに値を送ったりチャネルから値を取ったりする。

ch := make(chan int) // チャネルを作って
ch <- 10 // チャネルに値を送って
i := <- ch // チャネルから値を受け取る

同じスレッドで ch <- 10i := <-ch をやるとデッドロックと言われる。実際に試してみたコードはこんな感じ。結果は 10 と出るだけ。

package main

import "fmt"

func main() {
    // チャネルを作る
    ch := make(chan int)
    // ゴルーチンを作って、その先でチャネルに値を送信
    go func() {
        ch <- 10
    }()
    // ゴルーチンで値が渡ってくるのを待って値を受け取る
    i := <-ch
    // 出力
    fmt.Println(i) // 10
}

別に <- は一回というわけではなく複数回も OK

package main

import "fmt"

func main() {
    // チャネルを作る
    ch := make(chan int)
    // ゴルーチンを作って、その先でチャネルに値を送信
    go func() {
        ch <- 10
    }()
    go func() {
        ch <- 20
    }()
    // ゴルーチンで値が渡ってくるのを待って値を受け取る
    i, j := <-ch, <-ch
    // 出力
    fmt.Println(i, j)
}

実行すると 10 2020 10 と表示されると思うんだけど、何回実行しても 20 10 だった。

チャネルには複数の値を突っ込めるけど、沢山詰め込まれたらブロックするようにバッファーを指定できる。make 関数の第二引数にバッファーのサイズを指定する。

package main

import "fmt"

func main() {
    // チャネルを作る
    ch := make(chan int, 2) // バッファーサイズは 2
    // 2 回値を送るのは OK
    ch <- 10
    ch <- 20
    // 1 つずつ取り出して表示
    fmt.Println(<-ch)
    fmt.Println(<-ch)
}

どうもバッファーのサイズを指定しないとバッファーのサイズが 0 と同じ動きをするのでそういうことなのだろう。

因みにバッファーサイズを 2 にして ch <- xx を 3 回やると…

package main

import "fmt"

func main() {
    // チャネルを作る
    ch := make(chan int, 2) // バッファーサイズは 2
    // 3 回値を送るのは NG
    ch <- 10
    ch <- 20
    ch <- 30
    // 1 つずつ取り出して表示
    fmt.Println(<-ch)
    fmt.Println(<-ch)
    fmt.Println(<-ch)
}

deadlock! エラーになる。

fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chan send]:
main.main()
        c:/Users/kaota/go/src/sample/main.go:11 +0xa2
exit status 2

チャネルを close することで受け手側が、もうチャネルにデータが来ないことを判別できる。close したチャネルにデータ送信すると panic になる。チャネルは close しなくても何も不都合はないので、データが終わるということを明示するときに close する。

package main

import "fmt"

func main() {
    // チャネルを作る
    ch := make(chan int)

    // 3 つデータを送って close
    go func() {
        ch <- 10
        ch <- 20
        ch <- 30
        close(ch)
    }()

    // 無限ループでデータが終わるまで読み込む
    for {
        v, ok := <-ch
        if !ok {
            break
        }

        fmt.Println(v)
    }
}

実行すると思った通りの結果になるはず

10
20
30

因みにチャネルに値がある限り実行するというのは range を使って以下のように書ける。普通はこっちのほうがいいと思う。

package main

import "fmt"

func main() {
    // チャネルを作る
    ch := make(chan int)

    // 3 つデータを送って close
    go func() {
        ch <- 10
        ch <- 20
        ch <- 30
        close(ch)
    }()

    // range を使うとすっきり
    for v := range ch {
        fmt.Println(v)
    }
}

select case は、チャネルに対する操作を case に記述すると準備ができたものを選んで実行する。複数同時に準備が出来てる場合はランダムに実行される。 例が難しい…。

package main

import (
    "fmt"
)

func main() {
    // チャネルを作る
    out := make(chan int)
    quit := make(chan int)
    // 3 つデータを送って quit
    go func() {
        fmt.Println("groutine started")
        out <- 10
        out <- 20
        out <- 30
        quit <- 0
        fmt.Println("groutine ended")
    }()

    for {
        endLoop := false
        select {
        case x := <-out:
            fmt.Println(x)
        case <-quit: // quit にデータが来たら終わりのフラグを立てる
            fmt.Println("Done!!")
            endLoop = true
        }

        if endLoop {
            break
        }
    }
    fmt.Println("main endied")
}

実行するとこんな感じ。

groutine started
10
20
30
Done!!
main endied

default が無いと、case のどれかが準備 OK になるまでブロックされるけど default があるとブロックされない。

default が無いケース

package main

import (
    "fmt"
    "time"
)

func main() {
    // チャネルを作る
    out := make(chan int)
    quit := make(chan int)
    // 少し間隔をあけてデータを 3 つ送った後 quit
    go func() {
        fmt.Println(time.Now(), "groutine started")
        out <- 10
        time.Sleep(10)
        out <- 20
        time.Sleep(10)
        out <- 30
        time.Sleep(10)
        quit <- 0
        fmt.Println(time.Now(), "groutine ended")
    }()

    for {
        endLoop := false
        select {
        case x := <-out:
            fmt.Printf("%v: %v\n", time.Now(), x)
        case <-quit:
            fmt.Println(time.Now(), " Done!!")
            endLoop = true
        }

        if endLoop {
            break
        }
    }
    fmt.Println(time.Now(), "main endied")
}

実行するとこんな感じ。

2018-10-21 20:20:15.7192278 +0900 JST m=+0.003009901 groutine started
2018-10-21 20:20:15.7682091 +0900 JST m=+0.051991301: 10
2018-10-21 20:20:15.7701806 +0900 JST m=+0.053962801: 20
2018-10-21 20:20:15.7712797 +0900 JST m=+0.055061901: 30
2018-10-21 20:20:15.7731815 +0900 JST m=+0.056963701 groutine ended
2018-10-21 20:20:15.7731815 +0900 JST m=+0.056963701 Done!!
2018-10-21 20:20:15.7731815 +0900 JST m=+0.056963701 main endied

ちゃんと待ってる。default を追加してみる。

package main

import (
    "fmt"
    "time"
)

func main() {
    // チャネルを作る
    out := make(chan int)
    quit := make(chan int)
    // 少し間隔をあけてデータを 3 つ送った後 quit
    go func() {
        fmt.Println(time.Now(), "groutine started")
        out <- 10
        time.Sleep(10)
        out <- 20
        time.Sleep(10)
        out <- 30
        time.Sleep(10)
        quit <- 0
        fmt.Println(time.Now(), "groutine ended")
    }()

    for {
        endLoop := false
        select {
        case x := <-out:
            fmt.Printf("%v: %v\n", time.Now(), x)
        case <-quit:
            fmt.Println(time.Now(), "Done!!")
            endLoop = true
        default:
            fmt.Println(time.Now(), "no data yet")
        }

        if endLoop {
            break
        }
    }
    fmt.Println(time.Now(), "main endied")
}

実行するとこうなる。待たないのでデータが無いというログが沢山でる。

2018-10-21 20:22:08.3111922 +0900 JST m=+0.004003201 no data yet
2018-10-21 20:22:08.3581872 +0900 JST m=+0.050998201 no data yet
2018-10-21 20:22:08.3581872 +0900 JST m=+0.050998201 no data yet
2018-10-21 20:22:08.3581872 +0900 JST m=+0.050998201 no data yet
2018-10-21 20:22:08.3581872 +0900 JST m=+0.050998201 no data yet
2018-10-21 20:22:08.3581872 +0900 JST m=+0.050998201 no data yet
2018-10-21 20:22:08.3581872 +0900 JST m=+0.050998201 no data yet
2018-10-21 20:22:08.3581872 +0900 JST m=+0.050998201 no data yet
2018-10-21 20:22:08.3581872 +0900 JST m=+0.050998201 no data yet
2018-10-21 20:22:08.3581872 +0900 JST m=+0.050998201 no data yet
2018-10-21 20:22:08.359187 +0900 JST m=+0.051998001 no data yet
2018-10-21 20:22:08.359187 +0900 JST m=+0.051998001 no data yet
2018-10-21 20:22:08.359187 +0900 JST m=+0.051998001 no data yet
2018-10-21 20:22:08.359187 +0900 JST m=+0.051998001 no data yet
2018-10-21 20:22:08.359187 +0900 JST m=+0.051998001 no data yet
2018-10-21 20:22:08.359187 +0900 JST m=+0.051998001 no data yet
2018-10-21 20:22:08.3601888 +0900 JST m=+0.052999801 no data yet
2018-10-21 20:22:08.3601888 +0900 JST m=+0.052999801 no data yet
2018-10-21 20:22:08.3601888 +0900 JST m=+0.052999801 no data yet
2018-10-21 20:22:08.3601888 +0900 JST m=+0.052999801 no data yet
2018-10-21 20:22:08.3611868 +0900 JST m=+0.053997801 no data yet
2018-10-21 20:22:08.3611868 +0900 JST m=+0.053997801 no data yet
2018-10-21 20:22:08.3611868 +0900 JST m=+0.053997801 no data yet
2018-10-21 20:22:08.3611868 +0900 JST m=+0.053997801 no data yet
2018-10-21 20:22:08.3611868 +0900 JST m=+0.053997801 no data yet
2018-10-21 20:22:08.3621869 +0900 JST m=+0.054997901 no data yet
2018-10-21 20:22:08.3621869 +0900 JST m=+0.054997901 no data yet
2018-10-21 20:22:08.3621869 +0900 JST m=+0.054997901 no data yet
2018-10-21 20:22:08.3621869 +0900 JST m=+0.054997901 no data yet
2018-10-21 20:22:08.3631903 +0900 JST m=+0.056001301 no data yet
2018-10-21 20:22:08.3631903 +0900 JST m=+0.056001301 no data yet
2018-10-21 20:22:08.3631903 +0900 JST m=+0.056001301 no data yet
2018-10-21 20:22:08.3631903 +0900 JST m=+0.056001301 no data yet
2018-10-21 20:22:08.3631903 +0900 JST m=+0.056001301 no data yet
2018-10-21 20:22:08.3641871 +0900 JST m=+0.056998101 no data yet
2018-10-21 20:22:08.3641871 +0900 JST m=+0.056998101 no data yet
2018-10-21 20:22:08.3641871 +0900 JST m=+0.056998101 no data yet
2018-10-21 20:22:08.3641871 +0900 JST m=+0.056998101 no data yet
2018-10-21 20:22:08.3641871 +0900 JST m=+0.056998101 no data yet
2018-10-21 20:22:08.365188 +0900 JST m=+0.057999001 no data yet
2018-10-21 20:22:08.365188 +0900 JST m=+0.057999001 no data yet
2018-10-21 20:22:08.365188 +0900 JST m=+0.057999001 no data yet
2018-10-21 20:22:08.365188 +0900 JST m=+0.057999001 no data yet
2018-10-21 20:22:08.365188 +0900 JST m=+0.057999001 no data yet
2018-10-21 20:22:08.3661872 +0900 JST m=+0.058998201 no data yet
2018-10-21 20:22:08.3661872 +0900 JST m=+0.058998201 no data yet
2018-10-21 20:22:08.368188 +0900 JST m=+0.060999001 no data yet
2018-10-21 20:22:08.3111922 +0900 JST m=+0.004003201 groutine started
2018-10-21 20:22:08.368188 +0900 JST m=+0.060999001 no data yet
2018-10-21 20:22:08.3691896 +0900 JST m=+0.062000601: 10
2018-10-21 20:22:08.3691896 +0900 JST m=+0.062000601 no data yet
2018-10-21 20:22:08.3691896 +0900 JST m=+0.062000601 no data yet
2018-10-21 20:22:08.3691896 +0900 JST m=+0.062000601 no data yet
2018-10-21 20:22:08.3701879 +0900 JST m=+0.062998901 no data yet
2018-10-21 20:22:08.3701879 +0900 JST m=+0.062998901 no data yet
2018-10-21 20:22:08.3701879 +0900 JST m=+0.062998901 no data yet
2018-10-21 20:22:08.3701879 +0900 JST m=+0.062998901 no data yet
2018-10-21 20:22:08.3711933 +0900 JST m=+0.064004301: 20
2018-10-21 20:22:08.3711933 +0900 JST m=+0.064004301 no data yet
2018-10-21 20:22:08.3711933 +0900 JST m=+0.064004301 no data yet
2018-10-21 20:22:08.3711933 +0900 JST m=+0.064004301 no data yet
2018-10-21 20:22:08.372192 +0900 JST m=+0.065003001 no data yet
2018-10-21 20:22:08.372192 +0900 JST m=+0.065003001 no data yet
2018-10-21 20:22:08.372192 +0900 JST m=+0.065003001 no data yet
2018-10-21 20:22:08.372192 +0900 JST m=+0.065003001 no data yet
2018-10-21 20:22:08.372192 +0900 JST m=+0.065003001 no data yet
2018-10-21 20:22:08.372192 +0900 JST m=+0.065003001 no data yet
2018-10-21 20:22:08.3731933 +0900 JST m=+0.066004301: 30
2018-10-21 20:22:08.3731933 +0900 JST m=+0.066004301 no data yet
2018-10-21 20:22:08.3731933 +0900 JST m=+0.066004301 no data yet
2018-10-21 20:22:08.3731933 +0900 JST m=+0.066004301 no data yet
2018-10-21 20:22:08.3731933 +0900 JST m=+0.066004301 no data yet
2018-10-21 20:22:08.3741897 +0900 JST m=+0.067000701 no data yet
2018-10-21 20:22:08.3741897 +0900 JST m=+0.067000701 no data yet
2018-10-21 20:22:08.3741897 +0900 JST m=+0.067000701 no data yet
2018-10-21 20:22:08.3741897 +0900 JST m=+0.067000701 no data yet
2018-10-21 20:22:08.3751896 +0900 JST m=+0.068000601 Done!!
2018-10-21 20:22:08.3751896 +0900 JST m=+0.068000601 main endied

sync.Mutex を使うことでロックしたりアンロックしたりすることが出来る。他の言語のものと大体同じ。

まずは Mutex 無しで。複数スレッドから1000回インクリメントして1000回デクリメントするだけのプログラム。

package main

import (
    "fmt"
    "time"
)

type Counter struct {
    value int
}

func (c *Counter) Incr() {
    c.value++
}

func (c *Counter) Decr() {
    c.value--
}

func (c *Counter) Value() int {
    return c.value
}

func main() {
    c := Counter{}
    for i := 0; i < 1000; i++ {
        go c.Incr()
        go c.Decr()
    }

    time.Sleep(time.Second)
    fmt.Println(c.Value())
}

実行すると、その時々で変わるけど大体 0 にならない。例えば今実行したら 1 になった。もう一回実行すると -5 になった。 Inc, Dec, Value メソッドが同時に 1 つしか実行されないように Mutex を使うようにしてみた。ロックされっぱなしは困るので、ここでは defer を使って確実にメソッドの終わりで Unlock されるようにしてる。

package main

import (
    "fmt"
    "sync"
    "time"
)

type Counter struct {
    value int
    mutex sync.Mutex
}

func (c *Counter) Incr() {
    c.mutex.Lock()
    defer c.mutex.Unlock()
    c.value++
}

func (c *Counter) Decr() {
    c.mutex.Lock()
    defer c.mutex.Unlock()
    c.value--
}

func (c *Counter) Value() int {
    c.mutex.Lock()
    defer c.mutex.Unlock()
    return c.value
}

func main() {
    c := Counter{}
    for i := 0; i < 1000; i++ {
        go c.Incr()
        go c.Decr()
    }

    time.Sleep(time.Second)
    fmt.Println(c.Value())
}

何回実行しても 0 になる。期待した通り。

A Tour of Go を終えて

次は以下のドキュメントや動画がお勧めされてる。どれ見ようかなぁ。