参考文档:
https://www.liwenzhou.com/posts/Go/14_concurrence/
http://www.5lmh.com/并发编程/channel.html
示例一:
package main
import (
"fmt"
"sync"
)
var wg sync.WaitGroup
func main() {
wg.Add(2)
go nobufChannel() //不带缓冲区的初始化
go bufChannel() //有缓冲区的通道
wg.Wait()
}
//不带缓冲区的初始化
func nobufChannel() {
defer wg.Done()
channel1 := make(chan int)
go func() {
x := <-channel1
fmt.Println("channel里取出的数字:", x)
close(channel1)
}()
channel1 <- 10
fmt.Println("10存入channel")
}
//有缓冲区的通道
func bufChannel() {
defer wg.Done()
channel2 := make(chan int, 1) //指定了只能放一个数
channel2 <- 10
fmt.Println("10 发送到通道用bufChannel")
//channel2 <- 20 //todo 指定了只能放一个数,取出后,才能继续向通道里存入数据
//fmt.Println("20 发送到通道用bufChannel")
ch2 := <-channel2
fmt.Println("取出bufChannel中的数据,", ch2)
}
示例二:
package main
import (
"fmt"
"sync"
)
var wg sync.WaitGroup
var once sync.Once
func main() {
fmt.Println("我是返回值")
a := make(chan int, 100)
b := make(chan int, 100)
wg.Add(3)
go setValue(a)
go getValue(a, b)
go getValue(a, b)
for ret :=range b{
fmt.Println(ret) //循环打印b通道中的数据
}
wg.Wait()
}
func setValue(a chan int) {
defer wg.Done()
//循环的存入通道中
for i := 1; i <= 100; i++ {
a <- i
}
close(a)
}
func getValue(a, b chan int) {
defer wg.Done()
for {
v,ok:= <-a
if !ok {
break
}
b <- v //循环读出a通道中的数据,再的存入b通道中
}
once.Do(func(){close(b)})
}
close()
可以通过内置的close()函数关闭channel(如果你的管道不往里存值或者取值的时候一定记得关闭管道)
package main
import "fmt"
/*
1.对一个关闭的通道再发送值就会导致panic。
2.对一个关闭的通道进行接收会一直获取值直到通道为空。
3.对一个关闭的并且没有值的通道执行接收操作会得到对应类型的零值。
4.关闭一个已经关闭的通道会导致panic。
*/
func main() {
a := make(chan int, 2)
a <- 10
a <- 20
close(a)
//for k := range a {
// fmt.Println(k)
//}
v, ok := <-a
fmt.Println(v, ok) //10 true
v, ok = <-a
fmt.Println(v, ok) //20 true
v, ok = <-a
fmt.Println(v, ok) //0 false
v, ok = <-a
fmt.Println(v, ok) //0 false
}
如何优雅的从通道循环取值
当通过通道发送有限的数据时,我们可以通过close函数关闭通道来告知从该通道接收值的goroutine停止等待。当通道被关闭时,往该通道发送值会引发panic,从该通道里接收的值一直都是类型零值。那如何判断一个通道是否被关闭了呢?
我们来看下面这个例子:
// channel 练习
func main() {
ch1 := make(chan int)
ch2 := make(chan int)
// 开启goroutine将0~100的数发送到ch1中
go func() {
for i := 0; i < 100; i++ {
ch1 <- i
}
close(ch1)
}()
// 开启goroutine从ch1中接收值,并将该值的平方发送到ch2中
go func() {
for {
i, ok := <-ch1 // 通道关闭后再取值ok=false
if !ok {
break
}
ch2 <- i * i
}
close(ch2)
}()
// 在主goroutine中从ch2中接收值打印
for i := range ch2 { // 通道关闭后会退出for range循环
fmt.Println(i)
}
}
从上面的例子中我们看到有两种方式在接收值的时候判断通道是否被关闭,我们通常使用的是for range的方式。
通道总结
channel常见的异常总结,如下图:
注意:关闭已经关闭的channel也会引发panic。