使用channel
协程间通讯的普通使用, 发送值给channel
, 外部channel
接收.
func t1() {
ch := make(chan int)
go func() {
ch <- 1
}()
// <-ch // 导致下面语句阻塞
fmt.Println("channel int", <-ch)
}
复制代码
channel int 1
复制代码
channel
支持缓冲区
func t2() {
ch := make(chan int, 3)
go func() {
ch <- 1
ch <- 2
ch <- 3
ch <- 4 // 会阻塞写不入, 除非ch已接收
close(ch) // 关闭后不能再写入, 并且缓冲区内为空时则返回零值
}()
fmt.Println("buffer channel int",
<-ch,
<-ch,
<-ch,
<-ch,
<-ch)
}
复制代码
buffer channel int 1 2 3 4 0
复制代码
select
是专门给管道定制
func t3() {
ch1 := make(chan int)
ch2 := make(chan struct{})
go func() {
ch1 <- 2
}()
select {
case <-ch1:
fmt.Println("select here is ch1")
case <-ch2:
fmt.Println("select here is ch2")
}
}
复制代码
select here is ch1
复制代码
使用for
func t4() {
ch := make(chan int, 5)
go func() {
for i:=1; i<=5; i++ {
time.Sleep(time.Millisecond * 10)
ch <- i
}
}()
for v := range ch { // 会阻塞
fmt.Println("for channel ", v)
if v == 5 {
break
}
}
}
复制代码
for channel 1
for channel 2
for channel 3
for channel 4
for channel 5
复制代码
同时使用select
和for
func t5() {
chPrint := make(chan struct{})
chStop := make(chan struct{})
go func(){
time.Sleep(time.Second * 1)
chPrint <- struct{}{}
time.Sleep(time.Second * 1)
chPrint <- struct{}{}
time.Sleep(time.Second * 1)
chStop <- struct{}{}
}()
var sum int
for {
time.Sleep(time.Millisecond)
select {
case <-chPrint:
fmt.Println("for+select now is", sum)
case <-chStop:
fmt.Println("for+select stop, result is", sum)
return
default:
if sum == 10000 {
fmt.Println("for+select end, result is", sum)
return
}
sum += 1
}
}
}
复制代码
for+select now is 766
for+select now is 1540
for+select stop, result is 2309
复制代码
判断管道是否关闭
func t6() {
ch := make(chan struct{})
go func() {
close(ch)
//ch <- struct{}{} // 只运行这句, 输出OK
}()
if _, ok := <-ch; ok {
fmt.Println("if channel is ok")
return
}
fmt.Println("if channel is bad")
}
复制代码
if channel is bad
复制代码
以只发送或只接收为传递参数, 同理也可以为返回值
func t7() {
ch := make(chan struct{})
chExit := make(chan struct{})
go func(chRecv <-chan struct{}) {
fmt.Println("recv channel")
<-chRecv
chExit<- struct{}{}
}(ch)
go func(chSend chan<- struct{}) {
fmt.Println("send channel")
chSend <- struct{}{}
}(ch)
<-chExit
}
复制代码
send channel
recv channel
复制代码
channel机制流程汇总
makechan()
初始化hchan
结构体, 如果没有缓冲区即分配hchanSize
大小的内存并返回;而有缓冲区的情况下, 则计算管道元素类型大小并分配hchanSize
+(elem.size
* size
)大小的内存(缓冲区是一个环形的结构设计), 最后返回hchan
.
chansend()
向channel
发送数据, 首先锁住当前协程, 有如下几种情况, 按顺序判断:
- 有正在等待的接收者, 就立即转发给该接收者, 释放锁并退出.
- 有可用的缓冲区就将该值移到目标缓冲区等待被接收, 释放锁并退出.
- 是非阻塞(用于select)就退出, 释放锁并退出.
- 阻塞当前协程, 并将当前协程放到发送等待队列中并释放锁, 唤醒后清理sudog.
chanrecv()
接收channel
的数据, 首先锁住当前协程, 有如下几种情况, 按顺序判断:
channel
关闭并没有缓冲数据, 接收者接收的值将会是零值, 释放锁并退出.- 发送队列有发送者, 就立即接收该数据, 释放锁并退出.
- 有缓冲数据就将该数据复制给接收者, 释放锁并退出.
- 是非阻塞(用于select)就退出, 释放锁并退出.
- 阻塞当前协程, 并将当前协程放到发送等待队列中并释放锁, 唤醒后清理sudog.
跟
chansend
差不多, 多了个已关闭并没有缓冲数据的判断.
closechan
关闭channel
, 首先也要获取锁, 关闭管道并释放所有接收和发送的队列并清醒所有sudog.
但缓冲区的数据不会清理, 随时等待被接收.
有疑问加站长微信联系(非本文作者)