golang基础小记(23)——并发之channel、select多路复用

channel(通道)

goroutinechannel是 Go 语言秉承的 CSP(Communicating Sequential Process)并发模式的重要实现基础。该并发模式提倡通过通信共享内存而不是通过共享内存实现通信
channel可以实现在多个goroutine之间进行通信,它是一种特殊的类型,遵循先入先出规则。每一个channel只能传递指定元素类型的数据。

声明channel

channel是引用类型,声明channel的格式如下:

var 变量名 chan 元素类型

示例:

var ch chan bool // 声明一个传递布尔值的通道

声明得到的通道的值是nil

创建channel

通道使用make函数初始化,其格式如下:

make(chan 元素类型, [缓冲大小])

示例:

ch1 := make(chan int) // 未设置缓冲大小,默认是0,所以ch1是无缓冲通道

channel操作

使用之前创建的ch1

  • 发送:ch1 <- 10,把10发送到ch1中;
  • 接收:<-ch1,值可以用变量接收;
  • 关闭:close(ch),只有在所有数据都发送完毕的时候才需要关闭通道,但关闭通道不是必须的,其可以被垃圾回收机制回收。

已关闭通道的特点

  • 发送值会引发panic
  • 可以接收值直到通道为空,通道为空时继续接收会得到对应类型的零值;
  • 再次关闭该通道会引发panic

无缓冲通道

无缓冲的通道就是缓冲大小为0,因为没有缓冲,所以无缓冲通道在发送值的同时必须有一方在接收值,也就是说,接收值和发送值的操作需要在两个 goroutine中,否则程序就会因无限等待而死锁。

死锁示例:

func main() {
	ch := make(chan int)
	ch <- 10
	fmt.Println("发送成功")
}

输出:

fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chan send]:
main.main()
	e:/go/src/github.com/BattleL/studygo/day07/04channel/main.go:20 +0x5b
exit status 2

启用一个goroutine解决死锁问题:

func main() {
	ch := make(chan int)
	go func() {
		<-ch
	}()
	ch <- 10
	fmt.Println("发送成功") // 发送成功
}

程序正常运行。

有缓冲通道

有缓冲通道,就是缓冲大小>0,如果设置为3,那么该通道就最多能存放3个元素,存满就会阻塞,直到有元素被接收。

示例:

func main() {
	ch := make(chan int, 1)
	ch <- 10
	fmt.Println("发送成功") // 发送成功
}

内置的len函数可以得到通道的元素数量,cap函数可以得到通道的容量。

遍历通道

我们通常使用for range遍历通道,但要注意通道必须在发送完毕后关闭,否则会陷入死锁。
遍历方式:

for i := range ch {
	// i就是接收的值
}

判断通道关闭

通道接收时可以有两个返回值:

i, ok := <-ch

其中i是接收值,ok在接收成功时是true,在通道关闭且通道为空时是false
可以利用ok,结合for循环实现通道遍历。

单向通道

当通道作为参数传递时,我们可能希望限制通道只能发送或只能接收,这就需要单向通道。
out参数只能发送,in参数只能接收,函数名为test,无返回值:

func test(out chan<- int, in <-chan int) {}

其中:

  • chan<- int是int类型只写单向通道,只能发送不能接收;
  • <-chan int是int类型只读单向通道,只能接收不能发送。

注意:双向通道可以转换为单向通道,但反过来不行。

select多路复用

select可以用于同时从多个通道随机接收数据,其类似于switch语句,有一系列case分支和一个默认的分支。每个case可以对应一个通道的接收/发送操作。如果没有默认分支,select会一直等待,直到能够完成某个case的操作,如果多个分支都能完成,则随机选择一个。
示例:

func main() {
	ch := make(chan int, 1)
	for i := 0; i < 10; i++ {
		select {
		case x := <-ch:
			fmt.Println(x)
		case ch <- i:
		}
	}
}

输出:

0
2
4
6
8

如果将例子中的通道容量设为2,那么就会出现两个分支同时满足的情况,因为分支选择的随机性,输出将无法预测。
select的用途:

  • 提高代码可读性;
  • 处理一个或多个通道的发送/接收操作;
  • 多个分支同时满足时会随机选择(如果自己遍历判断,则规定了执行顺序);
  • 没有分支的select {}会一直等待,可以用于阻塞main函数。

参考文献

参考

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值