gorutine goルーチン
前に go をつけると並列処理できる
gorutineが終了しなくても、メインが終わればプログラムは終了してしまう。
それを避けるために sync.WaitGroupを使う。
並列処理の数だけAdd()を行う。(Doneが呼ばれる回数)
wg.Wait()でwg.Done()が呼ばれるまで待つ
func goroutine(s string, wg *sync.WaitGroup) { // defer wg.Done() for i := 0; i < 5; i++ { time.Sleep(100 * time.Millisecond) fmt.Println(s) } wg.Done() } func main() { var wg sync.WaitGroup wg.Add(1) // Doneは一回なので1を追加 // 並列処理 normalが終わる前に終わったら以降処理されない go goroutine("goroutine", &wg) normal("normal") // wg.Done()が呼ばれるまで待つ wg.Wait() }
channel チャネル
c := make(chan int) // chanの初期化、受け渡すデータの型にする c <- sum // returnできないのでchanに入れる x := <-c // chanから結果を取り出す、ブロッキングで値が入るのを待ってる、wg.Wait不要
一つのチャネルに値を入れていき、それを取り出すことも
func gorout1(s []int, c chan int) { sum := 0 for _, v := range s { sum += v } c <- sum // returnできないのでchanに入れる } func main() { s := []int{1, 2, 3, 4, 5} c := make(chan int) // chanの初期化 go gorout1(s, c) go gorout2(s, c) // chanから結果を取り出す x := <-c //ブロッキングで値が入るのを待ってる、Wait不要 fmt.Println(x) fmt.Println("----") y := <-c //2つの場合、どちらかを取得していく fmt.Println(y) }
Buffered Channel
channelの数を複数に、バッファの数を指定
ch := make(chan int, 2) //バッファの数は2
バッファの数以上chanに入れられない
チャネルから取り出すと1個減る
rangeで取り出すようなとき、あらかじめclose(ch)で閉じる
func main() { ch := make(chan int, 2) //バッファの数は2 ch <- 100 fmt.Println(len(ch)) ch <- 200 fmt.Println(len(ch)) // バッファの数以上のチャネルは入れられません // ch <- 300 // fmt.Println(len(ch)) x := <-ch // チャネルから取り出すと1個減る fmt.Println(x) fmt.Println(len(ch)) ch <- 300 // 追加すると増える fmt.Println(len(ch)) close(ch) for c := range ch { fmt.Println(c) } }
rangeとclose
rangeを使ってchannelを途中で取り出す
func main(s []int, c chan int) { sum := 0 for _, v := range s { sum += v c <- sum // 処理の途中でもrangeで受け取れる } close(c) // すべて受信したらchannelを閉じる }
すべて受信したらcloseでchannelを閉じる(deferを使うのもあり)
1個だけ受信ならいらない?
producerとconsumer
producerの結果をchannelに入れて、それをconsumerで処理する、それらを非同期処理する
producerもconsumerもgoルーチン
wg.Wait() // Add()したのが、すべてDone()されるのを待つ
close(ch) // consumer内のchannelがrangeのため、待ち続けるので終了する
fan-out fan-in
別のgoルーチンに渡す、それをまた別のgoルーチンにわたす
first -> second -> third
全部渡らなくても、受信したものからリアルタイムで渡せる
channelとselect
2つのgoルーチンからチャネルを受信してお互いにブロッキングしない
チャネルごとにselect caseで場合分けして、受け取れる
select { case msg1 := <-c1: fmt.Println(msg1) case msg2 := <-c2: fmt.Println(msg2) }
break
forの内側のselectをbreakで一気に抜けるには、breakに名前を指定します
break OuterLoop
Mutex
2つのgoルーチンから同時にアクセスしようとすると問題が起きるので、(タイミングによる)
通常の変数ではなくsync.Mutexを使った変数をtypeで用意する
type Counter struct { v map[string]int mux sync.Mutex } func (c *Counter) Inc(key string) { c.mux.Lock() // ロックする c.v[key]++ c.mux.Unlock() //ロック解除 }