Golang 通道(channel)学习二

上一节我们已经学习的通道的基本用法,也练习了缓冲通道和非缓冲通道。我们本届继续学习通道的其它功能特性。

单向通道

所谓单向通道,也就是只能向通道中发送数据,或只能从通道中读取数据。

按通道方向分 
单向通道
<-chan int 接收通道,从通道内读取通道
chan <- int 发送通道,向通道内发送内容
一般用在函数参数上,对函数内的通道行为做约束
双向通道
var c = make(chan int) 正常声明的通道
go会自动把双向通道转为单向通道

定义一个发送通道:

receiveChan := make(chan<-int,0)

单向通道的作用是什么

我们前面讲过,通道是用于传输数据的,那么通道如果只能发送或接收数据,那么就是这个通道只有一头能工作,比如作为一个单向的发送通道,不能从里面读取数据,又有什么用呢?

简单来说,单向通道是用来对代码做出限制,比如某些地方只希望从通道中读取数据,而不希望在这里也向通道写入数据。

package main

import "fmt"

func main() {
	ch := make(chan int,1)
	sentChan(ch)
	fmt.Println(<-ch)
}

func sentChan(ch chan <-int) {
	 ch <- 100
}

输出:100

从sentChan函数我们可以看出来,在函数里ch因为限制了是只能发送单向通道,所以在函数内不能从通道读取数据,如果写了 <-ch,那么编译器会报错:

Invalid operation: <- ch (receive from send-only type chan<- int) less... (Ctrl+F1) 
Inspection info: Reports incompatible types in binary and unary expressions.

但是在调用sentChan函数的代码中,ch却是一个正常的通道,既能读也能写。

这里是不是很巧妙,编程之美也就在于此,很优雅的就对代码做了安全的限制。

如果是将单向通道作为返回值返回,那么就是对调用方做了限制,拿到的返回通道,要么只能读,要么只能写。

select在通道上的使用

select是专门和通道配合使用的,效果和switch类似,选择符合的case执行。

语法格式:

        select {
	case 表达式1:代码
	case 表达式2:代码
	default: 代码
	}

如果没有case符合执行条件,那么会执行default分支

package main

import (
	"fmt"
	"math/rand"
)

func main() {
	chs := [3]chan int{
		make(chan int,1),
		make(chan int,1),
		make(chan int,1),
	}
	//返回一个取值范围在[0,3)的伪随机int值
	intn := rand.Intn(3)
	fmt.Println("ints :", intn)
	chs[intn] <- intn
	select {
	case <- chs[0]:
		fmt.Println("chan 0")
	case <- chs[1]:
		fmt.Println("chan 1")
	case <- chs[2]:
		fmt.Println("chan 2")
	default: fmt.Println("没有符合执行条件的代码")
	}
}

还有一些使用时要注意的点:

1、如果所有case都没有符合执行条件的,那么就执行default;

2、如果没有default,那么当前select就会阻塞,直到有通道满足表达式,则停止阻塞恢复执行;

3、如果select中有多个case满足执行条件,那么会随机选择一个执行;

4、如果从一个已经关闭的通道中读取数据,那么将引发panic,所以在需要的时候,要通过读取通道的第二个参数来判断通道是否可用;

类似这种写法:

        case _, ok := <-ch:
	if !ok {    
		fmt.Println("通道已关闭.")    
		break  
	}

5、每次执行select,select的case都会从上到下执行一遍所有表达式。

通道引发的panic

1、对一个已经关闭的通道,进行通道发送操作,会引发panic;

2、对已经关闭的通道再次关闭,会引发panic;