0
点赞
收藏
分享

微信扫一扫

GoLang之channel底层

霍华德 2022-04-05 阅读 94
golang

文章目录

GoLang之channel底层

注:本文以Go SDK v1.8进行讲解

1.channel类型介绍

2.接受命令

x := <- ch // 从ch中接收值并赋值给变量x
<-ch       // 从ch中接收值,忽略结果

3.发送命令

ch <- 10 // 把10发送到ch中

4.关闭命令

close(ch)

5.channel异常

5.1异常介绍

image-20220224103715061

image-20220224111740731

image-20220224133007433

5.2nil 接受

image-20220224114355133

5.3nil 发送

image-20220224114524708

5.4nil 关闭

在这里插入图片描述

5.5空 接受

image-20220224120909514

5.5空 发送

image-20220224120642950

5.6空 关闭

image-20220224120518922

5.7满了 接受

func main() {
	ch := make(chan int, 2)
	ch <- 1
	ch <- 2
	var a int = <-ch
	fmt.Println(a) //输出:1
}

5.8满了 发送

image-20220224092950459

5.9满了 关闭

image-20220224115151863

5.10make无缓冲 接受

image-20220224115527104

5.11make无缓冲 发送

image-20220224094457591

5.12make无缓冲 关闭

image-20220224115356774

6.var创建

var 变量名称 chan 元素类型
var ch1 chan int   // 声明一个传递整型的通道
var ch2 chan bool  // 声明一个传递布尔型的通道
var ch3 chan []int // 声明一个传递int切片的通道
func main() {
	var ch chan int
	if ch == nil {
		fmt.Println("ch是nil") //ch是nil
	} else {
		fmt.Println("ch不是nil")
	}
	fmt.Println(ch)//<nil>
}

7.make创建不指定缓冲区

7.1错误使用

func main() {
	ch := make(chan int)
	if ch == nil {
		fmt.Println("ch是nil")
	} else {
		fmt.Println("ch不是nil") //ch不是nil
	}
	fmt.Println(ch) //0xc000018060
}

image-20220224092749919

7.2无goroutine错误解决

image-20220224130325232

7.3有goroutine错误解决

image-20220224102159567
image-20220224105355672

7.4有goroutine正确解决

image-20220224100256406
image-20220224100330484

image-20220224130531273

8.make创建指定缓冲区

image-20220106090808859
image-20220106090836144
image-20220224093151152
image-20220224104604603

9.var结合make创建不指定缓冲区

make(chan 元素类型, [缓冲大小])

image-20220123182534233

10.var结合make创建指定缓冲区

image-20220123183220729

11.接受返回两个参数

image-20220224131418534

image-20220224132251196

12.close函数应用

image-20220224103505091
image-20220224103453763

image-20220224131738026

13.hchan结构体

// hchan结构体位于runtime/chan.go文件
type hchan struct {
	qcount   uint           // 当前队列中剩余元素个数,即chan 里元素数量
	dataqsiz uint          // chan 底层循环数组的长度,即可以存放的元素个数
	buf      unsafe.Pointer //指向底层循环数组的指针, 只针对有缓冲的 channel
	elemsize uint16// chan 中每个元素的大小,用于在buf中定位元素位置
	closed   uint32// chan 是否被关闭的标志
	elemtype *_type // chan 中元素类型,用于数据传递过程中的赋值;
	// 队列下标,指示元素写入时存放到队列中的位置,已发送元素在循环数组中相对于底层数组的索引
	sendx    uint   // send index
	 //  队列下标,指示元素从队列该位置读出,已接收元素在循环数组中相对于底层数组的索引
	recvx    uint   // receive index
	// 等待读消息的goroutine队列,等待接收的 goroutine 队列,表示被阻塞的 goroutine,这些 goroutine 由于尝试读取 channel里数据而被阻塞。
	recvq    waitq  // list of recv waiters
	 // 等待写消息的goroutine队列,等待发送的 goroutine 队列,表示被阻塞的 goroutine,这些 goroutine 由于尝试向 channel 发送数据而被阻塞。
	sendq    waitq  // list of send waiters

	//互斥锁,保护 hchan 中所有字段,lock 用来保证每个读 channel 或写 channel 的操作都是原子的
	//一个channel同时仅允许被一个goroutine读写
	lock mutex
}

14.waitq双向链表

// waitq结构体位于runtime/chan.go文件
type waitq struct {
	first *sudog
	last  *sudog
}

15.环形队列

在这里插入图片描述

16.等待队列

15.chantype结构体

//chantype位于runtime/type.go文件
type chantype struct {
	typ  _type
	elem *_type
	dir  uintptr
}

16.makechan函数

// makechan函数位于runtime/chan.go文件
func makechan(t *chantype, size int) *hchan {
	elem := t.elem

	// compiler checks this but be safe.
	if elem.size >= 1<<16 {
		throw("makechan: invalid channel element type")
	}
	if hchanSize%maxAlign != 0 || elem.align > maxAlign {
		throw("makechan: bad alignment")
	}

	mem, overflow := math.MulUintptr(elem.size, uintptr(size))
	if overflow || mem > maxAlloc-hchanSize || size < 0 {
		panic(plainError("makechan: size out of range"))
	}

	
	var c *hchan
	//对于‘case mem == 0:’与‘case elem.ptrdata == 0:’只进行一次内存分配
	switch {
	//size 大小为 0(无缓冲类型)
	case mem == 0:
		// Queue or element size is zero.
		c = (*hchan)(mallocgc(hchanSize, nil, true))
		//1. 非缓冲型的,buf 没用,直接指向 chan 起始地址处
		//2. 缓冲型的,能进入到这里,说明元素无指针且元素类型为 struct{},也无影响
		c.buf = c.raceaddr()
    //如果元素类型不含指针
	case elem.ptrdata == 0:
		// Elements do not contain pointers.
		// Allocate hchan and buf in one call.
		c = (*hchan)(mallocgc(hchanSize+mem, nil, true))
		c.buf = add(unsafe.Pointer(c), hchanSize)
	default:
		// 进行两次内存分配操作
		c = new(hchan)
		c.buf = mallocgc(mem, elem, true)
	}

	c.elemsize = uint16(elem.size)
	c.elemtype = elem
	// 循环数组长度
	c.dataqsiz = uint(size)
	lockInit(&c.lock, lockRankHchan)

	if debugChan {
		print("makechan: chan=", c, "; elemsize=", elem.size, "; dataqsiz=", size, "\n")
	}
	// 返回 hchan 指针
	return c
}
//raceaddr方法位于runtime/chan.go文件
func (c *hchan) raceaddr() unsafe.Pointer {
	return unsafe.Pointer(&c.buf)
}

17.CSP

举报

相关推荐

0 条评论