Goでは、複数のgoroutine間でデータをやり取りするために、チャネル(channel)という仕組みが用意されています。チャネルを利用することで、goroutine間でデータの送受信を実行できます。
目次
チャネルを通じたデータの送受信
(受信ブロック)
package main
import "fmt"
func sum(s []int, ch chan int) {
sum := 0
for _, v := range s {
sum += v
}
// チャネルにデータを送信
ch <- sum
}
func main() {
// チャネルの作成
ch := make(chan int)
s := []int{1, 1, 1}
go sum(s, ch)
// チャネルからデータを受信(受信ブロック)
sum := <-ch
fmt.Println(sum)
}
sum := <-ch
の箇所で処理をブロッキングしているため、sync.WaitGroup
を利用して処理を待機する必要はありません。
3
チャネルのバッファ
(送信ブロック)
チャネルにバッファを付けると、受信準備ができていなくても、容量分だけチャネルに送信することができます。
チャネルの容量に空きが無くなると、送信側が処理待ち状態(送信ブロック)になります。
package main
import "fmt"
func main() {
// 容量2のチャネルを作成
ch := make(chan string, 2)
ch <- "aaaa"
ch <- "bbbb"
// 容量2なのでエラーになります。(fatal error: all goroutines are asleep - deadlock!)
// ch <- "cccc"
// チャネルからデータを受信
m1 := <-ch
fmt.Println(m1)
m2 := <-ch
fmt.Println(m2)
// 容量に空きができたので、送信できます。
ch <- "cccc"
m3 := <-ch
fmt.Println(m3)
}
aaaa
bbbb
cccc
送信専用・受信専用チャネル
定義のしかたによって、チャネルを送信専用・受信専用にすることができます。
chan
の右側に<-演算子
を記述すると送信専用になります。chan
の左側に<-演算子
を記述すると受信専用になります。
package main
import (
"fmt"
"sync"
)
// ch 送信専用
func test1(ch chan<- string, wg *sync.WaitGroup) {
defer wg.Done()
ch <- "aaaa"
}
// ch 受信専用
func test2(ch <-chan string, wg *sync.WaitGroup) {
defer wg.Done()
m := <-ch
fmt.Println(m)
}
func main() {
var wg sync.WaitGroup
ch := make(chan string)
wg.Add(2)
go test1(ch, &wg)
go test2(ch, &wg)
wg.Wait()
}
aaaa
rangeでチャネルがcloseされるまで受信
rangeを利用して、チャネルからデータを受信する例を示します。
close()
によってチャネルがクローズされるまで、データを受信し続けます。
package main
import "fmt"
func test(s [][]int, ch chan int) {
// チャネルを閉じる
defer close(ch)
for _, v := range s {
sum := 0
for _, v2 := range v {
sum += v2
}
// チャネルに送信
ch <- sum
}
}
func main() {
// チャネルの作成
ch := make(chan int)
s := [][]int{
{1, 1, 1},
{2, 2, 2},
{3, 3, 3},
}
go test(s, ch)
// チャネルが閉じられるまで受信
for i := range ch {
fmt.Println(i)
}
}
3
6
9
selectで複数チャネルを同時受信
selectを利用すると、複数チャネルから同時受信できます。
package main
import (
"fmt"
"time"
)
func test1(ch chan<- string) {
for {
ch <- "test1"
time.Sleep(2 * time.Second)
}
}
func test2(ch chan<- string) {
for {
ch <- "test2"
time.Sleep(4 * time.Second)
}
}
func test3(quit chan<- int) {
time.Sleep(10 * time.Second)
quit <- 0
}
func main() {
c1 := make(chan string)
c2 := make(chan string)
quit := make(chan int)
go test1(c1)
go test2(c2)
go test3(quit)
cnt := 0
for {
select {
case s1 := <-c1:
fmt.Println(s1)
case s2 := <-c2:
fmt.Println(s2)
case <-quit:
fmt.Println("quit")
return
default:
cnt = cnt + 1
fmt.Printf("(cnt: %v)\n", cnt)
time.Sleep(1 * time.Second)
}
}
}
(cnt: 1)
test1
test2
(cnt: 2)
(cnt: 3)
test1
(cnt: 4)
(cnt: 5)
(cnt: 6)
test1
test2
(cnt: 7)
(cnt: 8)
(cnt: 9)
test1
(cnt: 10)
test2
quit