go goroutine入門

「tour of go」の goroutineやってみた。goroutineを使うと別スレッドで処理をおこなえる。むずかしい話ではないとは思うけど自分なりにちゃんと読んだので理解した内容をメモしておく

GoroutinesとChannels

https://tour.golang.org/concurrency/1
https://tour.golang.org/concurrency/2

まず、以下のプログラムを実行すると何も画面に表示されずに終了してしまう。理由はgoroutineでスレッドが起動する前にメイン処理が終了してしまうため。見た目の問題だけでなく、hello()の中でファイル出力などを行なっても処理が終わるためファイルに何も書き込まれずに終了する。

package main

import "fmt"

func hello() {
    fmt.Println("hello")
}

func main() {
    go hello()
}

そこで、channelを使用すると別スレッドとも同期的に処理が行えるようになる。

package main

import "fmt"

func hello(c chan bool) {

    fmt.Println("hello")

    // 4. channelに値を送信
    c <-true
}

func main() {

    // 1. channelを生成
    c := make(chan bool)

    // 2. goroutine実行
    go hello(c)

    // 3. channelの値を受信するまで待ちに入る
    _ = <-c

    // 5. channelで値が受信されるので処理が続行されて終了
}

Buffered Channels

https://tour.golang.org/concurrency/3

容量を指定してchannelを作成する例。以下のプログラムを実行すると2つしか領域を確保してない状態で3つ目をセットしようとするとそこでエラーになる

package main

import "fmt"

func main() {
    ch := make(chan int, 2)
    ch <- 1
    ch <- 2
    ch <- 3 // エラー!
}

以下のように「<-ch」で値が取り出せるのでエラーにならない

package main

import "fmt"

func main() {

    ch := make(chan int, 2)
    ch <- 1
    ch <- 2

    fmt.Println(<-ch) // 1 を取り出してchの中身が1つになる
    fmt.Println(<-ch) // 2 を取り出してchの中身が0になる

    ch <- 3 // 容量オーバーしてないのでエラーにならない
}

Range and Close

https://tour.golang.org/concurrency/4

tour of goのコードのフィボナッチ関数の部分をなくすとやってるのは以下のようなこと。goroutineで5回channelに値を送信して、メイン側ではrangeでchannelを受信し続けて終了(closeがよばれたら)終了する。

package main

import "fmt"                                                                                                                                                                                                                                  

func hello(c chan int) {
    for i := 0; i < 5; i++ {
        c <- i
    }
    close(c)
}

func main() {
    c := make(chan int)
    go hello(c)
    for i := range c {
        fmt.Println(i)
    }
}

実行結果

0
1
2
3
4
5

Select、Default Selection

https://tour.golang.org/concurrency/5
https://tour.golang.org/concurrency/6

selectを使うとchannelから非同期に送信されてくる値をselectを使ってどのchannelから送信されたかを判定することができる。また、どのchanelも受信していない場合の処理はdefaultで処理を書くことができる。switch文と同じ感覚。

package main

import "fmt"

func myfunc(c chan int, q chan int) {
    for i := 0; i < 5; i++ {
       c <-i
    }
    q <- 1
}

func yourfunc(c chan int, q chan int) {
    for {
        select {
        case i := <-c:
            fmt.Println(i)
        case <-q:
            fmt.Println("quit")
            return                                                                                                                                                                                                                            
        default:
            fmt.Println("default")
            time.Sleep(100 * time.Millisecond)
        }
    }
}

func main() {

    c := make(chan int)
    q := make(chan int)

    go myfunc(c, q)
    yourfunc(c, q)
}

実行結果

default
0
1
default
2
3
default
4
quit

sync.Mutex

https://tour.golang.org/concurrency/9

これはひとつのchannelに同時に複数のスレッドから書き込んだ場合に競合が起きてしまうので、排他制御を行うための仕組みと理解した

ずらずら書いたけど以上です