go channel的一些边缘知识以及常规操作:go 的nil chan

chan 的类型

  • 有缓冲chan
  • 无缓冲chan
  • nil chan 我从来没有用过这个功能,但是想想哪些令人恶心的面试官,是不是觉得没用过也得准备 @_~
    可能前面两个你张口就来,但是对于第三个你可能需要稍加思索了,应该怎么定义一个nil chan,它可以被读吗?可以被写吗,它可以被close吗?

chan 的定义

// 有缓冲的chan
var ch1 =make(chan int,5)
// 无缓冲的chan 方式 1
var ch2 = make(chan int)
// 无缓冲的chan 方式 2
var ch3 make(chan int, 0)
// nil chan 的定义
var ch4 chan int

chan 的读写

有缓冲读

  • 在同一个协程内(仔细想想,其实应该没有人会这么干,但是一旦这么干会有什么结果呢):
  • 如果刚创建了就直接读,其实会导致程序崩溃
package main
import (
	"fmt"
)
func main()  {
	var ch3 = make(chan int,5)
	fmt.Println(<-ch3)
}
//-------------------------
/*
fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chan receive]:
main.main()
        /home/xd/code/demo/chan.go:7 +0x3c
exit status 2
*/
  • 不在同一个协程内
    如果chan里没有内容,读会阻塞。
package main
import (
	"fmt"
	"time"
)
func main()  {
	var ch3 = make(chan int,1)
	ch3<-2
	go func() {
		for  {
			fmt.Println("start reading")
			fmt.Println(<-ch3)
			fmt.Println("read done")
		}
	}()
time.Sleep(time.Second*4)
}
/*
start reading
2
read done
start reading
*/

有缓冲写

对于有缓冲的chan,即便是没有协程进行读取也可以一直写,直到写满为止,写满以后阻塞

package main
import (
	"fmt"
	"time"
)
func main()  {
	var ch3 = make(chan int,13)
	ch3<-2
	ch3<-2
	ch3<-2
	ch3<-2
	fmt.Println("write done: len= ", len(ch3))
	time.Sleep(time.Second*4)
}
/*
output
write done: len=  4
*/

无缓冲写

无缓冲写需要注意的点是:必须先有协程等着读,写才能生效,否则 deadlock,下面是例子

package main

import (
	"fmt"
	"time"
)

func main() {
	var ch3 = make(chan int)
	// 如果再这里进行先往无缓冲chan写会导致block,所以先注掉
	// fatal error: all goroutines are asleep - deadlock!
	// ch3 <- 2
	go func() {
		for  {
			fmt.Println("start reading")
			fmt.Println(<-ch3)
			fmt.Println("read done")
		}
	}()
	// 在这里写是正常的(最好先稍微sleep一下,以免上面的协程并没起来已经执行写了)
	ch3 <- 2
	time.Sleep(time.Second * 4)
}

无缓冲读

无缓冲读,当没有协程往里写的时候读会阻塞
代码详见无缓冲写

nil chan读写

读写未初始化的 chan 都会阻塞(deadlock
为什么是这样呢,源码的chanrecv和chansend()函数都有下面这几行代码?

func chanrecv(c *hchan, ep unsafe.Pointer, block bool) (selected, received bool) {
	...
	if c == nil {
		if !block {
			return
		}
		gopark(nil, nil, waitReasonChanReceiveNilChan, traceEvGoStop, 2)
		throw("unreachable")
	}
	...
}
package main
import (
	"fmt"
	"time"
)
func main() {
	var ch3 chan int
	fmt.Println(<-ch3)
	ch3<-1
	time.Sleep(time.Second * 4)
}
/*
fatal error: all goroutines are asleep - deadlock!
goroutine 1 [chan receive (nil chan)]:
main.main()
        /home/xd/code/demo/chan.go:8 +0x2d
exit status 2
*/

关闭chan

有(无)缓冲管道

关闭有无缓冲的chan都是常规操作,没什么
往已经关闭的chan内写数据会panic这算是常识了,就不说了(错误提示:panic: send on closed channel

只是不能重复关闭,重复关闭chan 会引发panic

package main
func main() {
	var ch1 = make(chan int, 1)
	close(ch1)
	close(ch1)
}
/*
panic: close of closed channel

goroutine 1 [running]:
main.main()
        /home/xd/code/demo/chan.go:5 +0x39
exit status 2
*/

关闭nil chan

关闭未初始化(nil)的chan会引发panic

package main
func main() {
	var ch2 chan int
	close(ch2)
}
/*
panic: close of nil channel
goroutine 1 [running]:
main.main()
        /home/xd/code/demo/chan.go:6 +0x31
exit status 2
*/

读已经关闭的chan

这里肯定是读取的是有缓冲或者无缓冲的chan,因为nil chan 你close的时候就panic了
可以一直读到值,无论里面是不是有数据
有数据:读取现有数据,读完读对应类型的空值
没有数据:直接读取对应类型的空值

怎么知道chan 有没有关闭,关闭以后只读有效数据怎么实现

package main
import (
	"fmt"
	"time"
)
func main() {
	var ch2 = make(chan int,5)
	ch2 <-2
	ch2 <-2
	close(ch2)
	go func() {
		for  {
			// ok 表示只有当channel无数据,且channel被close了,才会返回ok=false。
			// 目前如果chan里面有数据的情况下没有办法知道这个chan是不是关闭的。
			// 这就是为什么避免出现异常,要在生产者一端关闭chan的原因
			// 默认生产者关闭了以后肯定不会再写了。
			val ,ok := <-ch2
			if ok == true {
				fmt.Println(val)
			} else {
				break
			}
		}
	}()
	time.Sleep(time.Second)
}
/*
output
xd@xd:~/code/demo$ go run chan.go
2
2
chan is empty and closed
*/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值