PUROGU LADESU

ポエムがメインのブログです。

【Golang】 7 Goroutine

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() //ロック解除
}