27《Go语言入门》单向通道(chan)

这是我纯手写的《Go语言入门》,手把手教你入门Go。源码+文章,看了你就会🥴!
文章中所有的代码我都放到了github.com/GanZhiXiong/go_learning这个仓库中!
看文章时,对照仓库中代码学习效果更佳哦!

什么是单向通道

单向通道就是只能用于写入或读取数据的通道。
它是对通道的一种使用限制。

  • 如果channel是只能读取数据,那么它肯定是空的,因为你没机会往里面写入数据
  • 如果channel是只能写入数据,即使写进去了,也没有丝毫意义,因为没办法读取到里面的数据。

声明单向通道

  • 只能写入数据的通道
var 通道实例 chan<- 元素类型
  • 只能读取数据的通道
var 通道实例 <-chan 元素类型

示例:

func TestOnWayChan(t *testing.T) {
	ch := make(chan int)
	// 声明一个只能写入数据的通道,并赋值为ch
	var chSendOnly chan<- int = ch
	t.Log(chSendOnly)
	chSendOnly <- 0
	// Invalid operation: <-chSendOnly (receive from send-only type chan<- int)
	//<-chSendOnly
	
	// 声明一个只能读取数据的通道,并赋值为ch
	var chReadOnly <-chan int = ch
	t.Log(chReadOnly)
	// Invalid operation: chReadOnly <- 0 (send to receive-only type <-chan int)
	//chReadOnly <- 0
	<-chReadOnly
}

也可以使用make来创建:

func TestOneWayChanMake(t *testing.T) {
	// 一个不能写入数据只能读取的通道是毫无意义的,因为它是空的
	ch := make(<-chan int)
	var chReadOnly <-chan int = ch
	<-ch
	<-chReadOnly
}

time包中的单向通道

time包中计时器会返回一个Timer实例,如:

func TestOneWayChanTime(t *testing.T) {
	timer := time.NewTimer(time.Second)
	t.Log(timer)
}

Timer类型定义如下:

// The Timer type represents a single event.
// When the Timer expires, the current time will be sent on C,
// unless the Timer was created by AfterFunc.
// A Timer must be created with NewTimer or AfterFunc.
type Timer struct {
	C <-chan Time
	r runtimeTimer
}

第二行的C通道就是一个只能读取的单向通道。
如果此处不进行通道方向的约束,一单外部向通道写入数据,将会造成其他使用到计时器的地方逻辑产生混乱。

因此它有利于代码接口的严谨性。

关闭通道

通道是引用类型,和map类似,map在没有任何外部引用的时候,Go的运行时会自动对内存进行垃圾回收(Garbage Collection,GC)。

因此通道也可以被垃圾回收,也可以主动关闭。

关闭通道直接使用Go中内置的close()函数即可:

close(ch)

但是的通道仍然可以访问。

只读通道不能关闭

只读通道不能关闭,否者会编译报错

func TestOneWayChanClose(t *testing.T) {
	ch := make(<-chan int)
	t.Log(ch)
	// 只读通道不能关闭,否者会报错,Cannot use 'ch' (type <-chan int) as type chan<- Type
	//close(ch)

	ch1 := make(chan<- int)
	close(ch1)
}

给关闭通道发送数据将会触发panic

func TestChanClose(t *testing.T) {
	ch := make(chan int)
	close(ch)

	t.Logf("ptr: %p cap: %d len: %d", ch, cap(ch), len(ch))

	ch <- 1
}
=== RUN   TestChanClose
    27_one-way_chan_test.go:63: ptr: 0xc0000662a0 cap: 0 len: 0
--- FAIL: TestChanClose (0.00s)
panic: send on closed channel [recovered]
	panic: send on closed channel

从已关闭的通道接收数据时将不会发生阻塞

func TestChanClose1(t *testing.T) {
	ch := make(chan int, 2)
	ch <- 0
	ch <- 1
	close(ch)

	for i := 0; i < cap(ch)+2; i++ {
		v, ok := <- ch
		t.Log(v, ok)
	}
}
=== RUN   TestChanClose1
    27_one-way_chan_test.go:65: 0 true
    27_one-way_chan_test.go:65: 1 true
    27_one-way_chan_test.go:65: 0 false
    27_one-way_chan_test.go:65: 0 false
--- PASS: TestChanClose1 (0.00s)
PASS

从输出的结果可以看出:

  • 通道关闭后仍然可以访问内部数据
  • 通道关闭后,即便通道没有数据,在获取时也不会发生阻塞,但此时取出数据会失败(ok为false)

如果把上面代码中的close(ch)注释,结果是怎么样的呢?
结果如下:

=== RUN   TestChanClose1
    27_one-way_chan_test.go:64: ptr: 0xc0000e8000 cap: 2 len: 2
    27_one-way_chan_test.go:68: 0 true
    27_one-way_chan_test.go:68: 1 true
fatal error: all goroutines are asleep - deadlock!

重复关闭一个通道将会导致panic

func TestChanClose3(t *testing.T) {
	ch := make(chan int, 10)
	ch <- 1
	ch <- 2
	ch <- 3

	close(ch)
	close(ch)

	for v := range ch {
		fmt.Println(v)
	}
}
=== RUN   TestChanClose3
--- FAIL: TestChanClose3 (0.00s)
panic: close of closed channel [recovered]
	panic: close of closed channel

关闭nil值的通道将会导致panic

func TestChanClose4(t *testing.T) {
	var ch chan int
	t.Log(ch)
	close(ch)
}
=== RUN   TestChanClose4
    27_one-way_chan_test.go:102: <nil>
--- FAIL: TestChanClose4 (0.00s)
panic: close of nil channel [recovered]
	panic: close of nil channel

测试题

测试1

func TestChanClose2(t *testing.T) {
	ch := make(chan int, 10)
	ch <- 1
	ch <- 2
	ch <- 3

	close(ch)

	for v := range ch {
		fmt.Println(v)
	}
}

支持🤟


  • 🎸 [关注❤️我吧],我会持续更新的。
  • 🎸 [点个👍赞吧],码字不易麻烦了。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值