通道channel可以被认为是goroutine通信的管道。关于前一篇go语言锁的博客中也提到,解决临界资源问题可以使用锁,但是更建议的是使用channel来实现goroutine之间的通信。
不要通过共享内存来通信,而应该通过通信来共享内存。
每个通道都有其相关的类型,该类型就是通道内允许传输的数据类型。
空的通道为nil,nil的通道没有任何用处。所以通道的声明是和map类似的。
- //声明通道
- var channel_name chan type
- //初始化通道
- channel_name = make(chan type)
简短声明
channel_name := make(chan type)
- data := <- channel_name //将通道中的值赋值给变量
- channel_name <- data //将变量的值写入通道
-
- value, ok := <- channel_name //从一个channel中读取值
正常情况下,如果不使用sync.WaitGroup同步等待组、锁,主函数中的子goroutine很可能在还没有执行时,因为主函数的结束而一起结束了。
但是可以通过“channel的读写操作都是阻塞的这一特性”,子goroutine通过channel通道传递信息给主goroutine,主goroutine在读到数据前会一直阻塞。
- func main() {
- channel1 := make(chan bool)
- go func(){
- for i:=0;i<10;i++{
- fmt.Println(i)
- }
- channel1 <- true
- fmt.Println("子goroutine结束...")
- }()
-
- data1 := <- channel1
- fmt.Println("channel传递的值:",data1)
- fmt.Println("主函数结束...")
- }
发送者可以通过关闭channel通道,告知接收方不会有更多的数据被发送到channel上了。
close(channel_name)
接受者可以接受来自channel读到数据时,使用额外的变量来检查通道是否关闭。
ok为true时,表示从通道中读取了一个value;当ok是false是,意味着正从一个封闭的通道读取数据,读取到的value都将是零值。
value, ok := <- channel_name
- var channel1 = make(chan int)
-
- func main() {
- go fun1()
- for{
- fmt.Println("从通道中读取值...")
- v,ok := <-channel1
- time.Sleep(1*time.Second)
- if !ok {
- fmt.Println("channel读取结束,通道关闭",v,ok)
- break
- }
- fmt.Println("通道中的值:",v,ok)
- }
- }
-
- func fun1() {
- for i:=0;i<10;i++{
- time.Sleep(1*time.Second)
- fmt.Println("通道中写入值:", i)
- channel1 <- i
- }
- close(channel1)
- }
通过for_range增强循环,不需要自己判断通道是否关闭。range会判断通道是否关闭。
- var channel1 = make(chan int)
-
- func main() {
- go fun1()
- for v := range channel1 { // v <- channel1
- time.Sleep(1 * time.Second)
- fmt.Println(v)
- }
- }
-
- func fun1() {
- for i := 0; i < 10; i++ {
- channel1 <- i
- }
- close(channel1)
- }
带缓冲区的通道,会把数据先写入缓冲区中。发送数据时,只有当缓冲区中满了才会被阻塞;接收数据时,只有缓冲区为空的时候才会被阻塞。
ch := make(chan type, Size)
Size需要大于0,使通道具有缓冲区。默认情况无缓冲区通道的容量为0,所以省略了该参数。
- var channel1 = make(chan int,5) //创建一个容量5的缓冲通道
-
- func main() {
- go fun1()
- for v := range channel1 {
- time.Sleep(1 * time.Second)
- fmt.Println("\t读出数据",v)
- }
- }
-
- func fun1() {
- for i := 0; i < 10; i++ {
- channel1 <- i
- fmt.Println("写入数据:",i)
- }
- close(channel1)
- }
双向通道
前面我们创建的通道,都是双向通道。双向通道是可以,一个goroutine发送数据,一个goroutine接收数据的。
定向通道(单向通道)
单向通道也就是定向通道。单向通道只能发送或者接收数据。
定向通道创建语法
- channel1 := make(chan <-int) //只能写入数据,不能读数据
- channel2 := make(<- chan int) //只能读数据,不能写入数据
定向通道的用法
往往创建和使用通道的时候通常还是创建双向通道。单向通道只用来作为函数参数类型。
只是在将通道作为参数传递到函数时,函数中通道的类型使用单向通道。限制通道在函数内部使用时是只能读不能写的;或者是只能写不能读的。