Golang channel操作详解

序言

对于channel的操作始终有着这么一句口诀用于记忆,空读写阻塞,写关闭异常,读关闭空零。对于刚开始学习channel并不知道具体的解释,所以在查阅了部分资料后,一一做了实验。

1.channel的状态

  1. nil未初始化的状态,只进行了声明,或者手动赋值为nil (做了var操作但是没有进行make)
  2. active/open正常的channel,可读或者可写 (make后的有缓冲或者无缓冲channel
  3. closed已关闭,千万不要误认为关闭channel后,channel的值是nil

2.对nil状态和close状态的channel进行的验证

这一部分是对nil状态和close状态的channel进行的验证,结果和口诀一一对应

package main

import (
	"fmt"
)

// 空读写阻塞((deadlock!)),写关闭异常,读关闭空零
// nil,未初始化的状态,只进行了声明,或者手动赋值为nil (做了var操作但是没有进行make)
// active(open),正常的channel,可读或者可写 (make后的有缓冲或者无缓冲channel)
// closed,已关闭,千万不要误认为关闭channel后,channel的值是nil

// 空读写阻塞(nil channel)
func nilChanReadWrite(){
	var nilChan chan int
	// fatal error: all goroutines are asleep - deadlock!
	// 编译器提醒 Send may block because of 'nil' channel 会向一个nil channel发送数据阻塞
    nilChan <- 1

    // 运行错误同上
    // 编译器提醒 Receive may block because of 'nil' channel 从nil channel获取数据阻塞
    <- nilChan

    // panic: close of nil channel 也不能进行关闭会报错 panic
	close(nilChan)
}

// 读关闭空零(nil channel)
func readClose(){
	// 读关闭空零 读关闭的int->0 string->"" struct->{}
	chnClose := make(chan int, 1)
	close(chnClose)
	d := <- chnClose
	fmt.Println(d)
}

// 写关闭异常(nil channel)
func writeClose(){
	chnClose := make(chan int, 1)
	close(chnClose)
	// panic: send on closed channel
	chnClose <- 1
	fmt.Println(chnClose)
}

// 判断chan是否关闭
func isCloseChan() {
	c := make(chan int, 10)
	c <- 1
	c <- 2
	c <- 3
	close(c)
	for {
		i,  ok := <-c
		fmt.Println(ok)
		if !ok {
			fmt.Println("channel closed!")
			break
		}
		fmt.Println(i)
	}
}

func main() {
	nilChanReadWrite()
}

3.对active/open状态的channel进行测试

package main

import (
	"fmt"
)
// active(open) channel
// 非缓冲chan对于单个读写(只有读操作或者只有写操作)两端的goroutine都会阻塞(deadlock!),只有对一个非缓冲channel同时有读写操作时才不会阻塞(边读边写,读写速度不一样也可)。
// 缓冲chan在容量为空时,读端goroutine会阻塞(deadlock!);在容量未满时,读写两端都不会阻塞;但是容量满了之后,写端会阻塞

// 无缓冲读
func ReadNoDataFromNoBufCh() {
	noBufCh := make(chan int)

	<-noBufCh
	fmt.Println("read from no buffer channel success")

	// Output:
	// fatal error: all goroutines are asleep - deadlock!
}

// 无缓冲写
func WriteNoBufCh() {
	ch := make(chan int)

	ch <- 1
	fmt.Println(<-ch)
	fmt.Println("write success no block")

	// Output:
	// fatal error: all goroutines are asleep - deadlock!
}

// 有缓冲写
func WriteWithBufCh() {
	ch := make(chan int, 1)

	ch <- 1
	// 对已经满了的channel再写入会报错
	//ch <- 1
	fmt.Println("write success no block")

	// Output:
	// fatal error: all goroutines are asleep - deadlock!
}

// 有缓冲读
func ReadWithBufCh() {
	ch := make(chan int, 5)
	ch <- 1
	ch <- 1
	fmt.Println(<- ch)

	fmt.Println("write success no block")

	// Output:
	// fatal error: all goroutines are asleep - deadlock!
}


//  带缓冲/不带缓冲 阻塞循环读写
func BufReadWrite()  {
    // 只要读写端同时有操作,不管是否有缓冲都不会阻塞
	//ch := make(chan int, 10)
	ch := make(chan int, 5)
	go func() {
		for i := 0; i < 10; i++ {
			ch <- i
			fmt.Println("写了一个")
		}
	}()
	for i := 0; i < 10; i++ {
		fmt.Println(<-ch)
		fmt.Println("读了一个")
	}

}

// select用法
func ChanSelect()  {
	ch1 := make(chan int, 5)
	ch2 := make(chan int, 5)
	ch3 := make(chan int, 5)
	ch1 <- 1
	ch2 <- 2
	ch3 <- 3
	select {
		case <- ch1:
			fmt.Println("shoudao chn1")
		case <- ch2:
			fmt.Println("shoudao chn222")
		case <- ch3:
			fmt.Println("shoudao chn3333")
		case <-ch2:

		default:
			fmt.Println("de")
	}
}


func main() {
	BufReadWrite()
}

4.总结图(借用网上小伙伴 )

原图片地址
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值