Skip to content

golang concurrency

foolmacky edited this page Dec 1, 2021 · 3 revisions

やや古いが自分が美しいと思うパターン

func randomTime() time.Duration {
	return time.Duration(rand.Intn(1e3)) * time.Millisecond
}

func worker(msg string) <-chan string {
	receiver := make(chan string)
	for i := 0; i < 300; i++ {
		go func(i int) {
			time.Sleep(randomTime())
			msg := fmt.Sprintf("%d %s done", i, msg)
			receiver <- msg
		}(i)
	}
	return receiver
}

func main() {
	receiver := worker("job")
	for {
		select {
		case receive := <-receiver:
			log.Println(receive)
		case <-time.After(time.Second): // 一定時間経過したら受信
			log.Println("timeout")
			return // 受信時に終われば timeout 処理になる。
		}
	}
}
  • 並列数を制御するサンプル
func worker(msg string) <-chan string {
	limit := make(chan int, 5)
	receiver := make(chan string)
	go func() {
		for i := 0; i < 100; i++ {
			log.Println(runtime.NumGoroutine())
			limit <- 1
			go func(i int) {
				msg := fmt.Sprintf("%d %s done", i, msg)
				receiver <- msg
				<-limit
			}(i)
		}
	}()
	return receiver
}

func main() {
	receiver := worker("job")
	for i := 0; i < 100; i++ {
		log.Println(<-receiver)
	}
}

##いつも混乱する点を少し

  • contextでtimeoutしたり、キャンセルしたりするときは親routineでcontextを作成して、子routineに与えることで、子routineを制御する。作成した親自身を制御するのではない。
    そして、制御した結果は親routineに返ってくる
  • closureで記載した場合は、子routineに親routineの変数が見えるが、子routineがキャンセルされたことが通知されても、その瞬間は子routineは生きているかもしれないので、両者で共有している変数に書き込んだりすると RACEエラーが発生する可能性がある
  • goroutine間のデータのやり取りは基本的にchannelを通じて行うべきだと思う。

Clone this wiki locally