Golang channel操作详解

  • Post author:
  • Post category:golang




序言

对于

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.总结图(借用网上小伙伴 )


原图片地址


在这里插入图片描述



版权声明:本文为bigdaddy_maybe原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。