这是我纯手写的《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)
}
}
支持🤟
- 🎸 [关注❤️我吧],我会持续更新的。
- 🎸 [点个👍赞吧],码字不易麻烦了。