先说这个buffered chan就是在创建chan的时候加上个chan buffer的大小
ch := make(chan string, 5)
基本的chan在send的时候如果没有来receive,send的也会block住。但是带buffer的就不同了,再send的时候,如果buffer没有满,send方不会被block住。receive的时候,再buffer不空的时候不会receiver不会block,这个和基本的chan没有啥区别。(chan如果被关闭了,这时候读receive是不会block的,但是可以用一个用另外一个bool来确定当前读成功了没有)
另外channel还可以做成单向的,只能读/写。
func writeOnly(ch chan<- string) {
ch <- "hahaha"
// v := <- ch // invalid
}
func readOnly(ch <-chan string) {
for v := range ch {
fmt.Println(v)
}
}
func main() {
ch1 := make(chan string)
go writeOnly(ch1)
readOnly(ch1)
}
有了chan,基本是配合select来用。
select {
case cmd := <-ch1:
// Handle ...
case cmd := <-ch2:
...
case cmd := <-chStop:
// stop server
}
如果所有的ch都没有数据,那这个select就会等在这里。如果有一个有,就会执行对应的case,并结束select模块。所以一般外面加个while。这个有点类似switch,但是目前是不支持fallthrough的。
另外支持default,如果所有的ch都没有数据准备好,就执行default。注意,如果进入一个case,不会再进来这个default。
下面是实例是两个生产者和一个消费者,消费者不是一直block,每隔一段时间醒一下,再实际应用中可以发送下status状态方便monitor。
func producer1(ch chan string) {
for i := 0; i < 10; i++ {
ch <- "1: " + strconv.FormatInt(int64(i), 10)
}
}
func producer2(ch chan string) {
for i := 0; i < 15; i++ {
ch <- "2: " + strconv.FormatInt(int64(i), 10)
}
}
func checker(ch chan string) {
for {
ch <- "wake up"
time.Sleep(time.Duration(1) * time.Second)
}
}
func consumer(ch1, ch2 chan string) {
for {
select {
case v := <- ch1:
fmt.Println(v)
case v2 := <- ch2:
fmt.Println(v2)
default:
fmt.Println("xxxx")
time.Sleep(time.Duration(1) * time.Second)
}
}
}
func main() {
ch1 := make(chan string, 3)
ch2 := make(chan string, 3)
go producer1(ch1)
go producer2(ch2)
consumer(ch1, ch2)
}
output:
xxxx
2: 0
1: 0
1: 1
2: 1
1: 2
1: 3
1: 4
1: 5
2: 2
2: 3
1: 6
1: 7
2: 4
1: 8
1: 9
2: 5
2: 6
2: 7
2: 8
2: 9
2: 10
2: 11
2: 12
2: 13
2: 14
xxxx
xxxx
xxxx
会一直下去。
但是这样的话,再default里面停等的时候即使ch1,ch2来了数据,也没有办法处理。所以我感觉这个default用法不应该是这样的。如果真的就是需要不断的unblock channel的话,可以另外用个ch来产生unblock的事件。像下面这样。
func producer1(ch chan string) {
for i := 0; i < 10; i++ {
ch <- "1: " + strconv.FormatInt(int64(i), 10)
}
}
func producer2(ch chan string) {
for i := 0; i < 15; i++ {
ch <- "2: " + strconv.FormatInt(int64(i), 10)
}
}
func checker(ch chan string) {
for {
ch <- "wake up"
time.Sleep(time.Duration(2) * time.Second)
}
}
func consumer(ch1, ch2, wake chan string) {
for {
select {
case v := <- ch1:
fmt.Println(v)
case v2 := <- ch2:
fmt.Println(v2)
case v3 := <- wake:
fmt.Println("xxxx", v3)
}
}
}
func main() {
ch1 := make(chan string, 3)
ch2 := make(chan string, 3)
wake := make(chan string)
go producer1(ch1)
go producer2(ch2)
go checker(wake)
consumer(ch1, ch2, wake)
}