知っておくべき 4 つのこととは?
- Goroutine
- Channel
- Select
- Sync パッケージ
です。
詳しく見ていきましょ。
1. Goroutine
関数やメソッドの前に go
を付けると、main()
とは違う場所(=スレッド)で処理される
go fmt.Println("別の場所で実行")
go SomeFunction()
main()
が終了すると、Goroutine も終了するので、これから説明する方法で「待つ」必要がある。
2. Channel
Goroutine でデータをやり取りをやりとりする仕組み
-
定義:
c := make(chan int)
- 送信専用も作れる (chan から出ていく矢印):
var receveOnly <-chan int
- 受信専用も作れる (chan に入っていく矢印):
var sendOnly chan<- int
- 送信専用も作れる (chan から出ていく矢印):
-
データ送る:
c <- 3
-
データ受け取る:
val := <-c
- データを受け取る準備ができていても、送られてこないと、送られてくるまで待つ
- ちなみに: 矢印(
<-
)とc
の間にスペースがないのが正しいフォーマット。あっても動作するけど。。
func main() {
ch := make(chan int)
fmt.Println("Goroutine 実行するよ!")
go func() {
ch <- 42
}()
fmt.Println("Goroutine 実行したよ!")
time.Sleep(2 * time.Second)
recieved := <-ch
fmt.Println(recieved)
}
すぐに受け取れなくても、channel を通して送られたデータはその後の処理でちゃんと受け取れる
3. Select
- 先に実行 (例: 送る、受け取る) できるものを処理する。
- 何も実行できる状態でなければ、
default
が実行される。 -
default
は書かなくてもよく、もし無ければ、何かが実行できるまで待つ
func main() {
c1 := make(chan int)
c2 := make(chan int)
go func() {
// 何かしらの処理後に c1 にデータを送る
time.Sleep(1 * time.Second)
c1 <- 10
// 何かしらの処理後に c2 にデータを送る
time.Sleep(1 * time.Second)
c2 <- 20
}()
// Goroutine を使い無限ループで待機
go func() {
for {
time.Sleep(300 * time.Millisecond)
select {
case v1 := <-c1:
fmt.Printf("recieved %v from c1\n", v1)
case v2 := <-c2:
fmt.Printf("recieved %v from c2\n", v2)
default:
fmt.Printf("Not Ready !!\n")
}
}
}()
// 無限ループを 3 秒間だけ回すため、main() で sleep する
time.Sleep(3 * time.Second)
}
-
time.After(duration)
でタイマー的なこともできる
func main() {
c1 := make(chan int)
c2 := make(chan int)
go func() {
// 何かしらの処理後に c1 にデータを送る
time.Sleep(1 * time.Second)
c1 <- 10
// 何かしらの処理後に c2 にデータを送る
time.Sleep(1 * time.Second)
c2 <- 20
}()
// time.After を使っているので、main() で無限ループしても、指定時間後に終了する
for {
time.Sleep(300 * time.Millisecond)
select {
case v1 := <-c1:
fmt.Printf("recieved %v from c1\n", v1)
case v2 := <-c2:
fmt.Printf("recieved %v from c2\n", v2)
case <-time.After(3 * time.Second):
fmt.Println("timed out !!")
return
}
}
}
4. Sync パッケージ
-
定義:
var wg sync.WaitGroup
-
追加:
wg.Add(1)
-
終了:
wg.Done()
-
wg.ADD
した分wg.Done
するまで、待つ:wg.Wait()
func main() {
// 定義
var wg sync.WaitGroup
for i := 0; i < 5; i++ {
// 追加
wg.Add(1)
go func(c int) {
// 処理が終わったら終了
defer wg.Done()
// 長い処理をしてると仮定
time.Sleep(2 * time.Second)
fmt.Println(c)
}(i)
}
// 全て Done するまで待つ
wg.Wait()
fmt.Println("Closing...")
}