Go语言Channel的底层原理详解

Channel

  • 1.简介
  • 2. channel的底层结构
  • 3.channel的基础用法
  • 4.Go的发送数据底层原理
  • 5.Go的接收数据底层原理
  • 6.Go中Channel的应用场景

1.简介

Go中有一句经典的名言:“不要通过共享内存的方式来通信,而要通过通信的方式来共享内存。”原因是因为:使用共享内存的方式会造成数据竞争,为了防止数据竞争所以需要加锁,共享内存的方式在高并发场景下的锁竞争激烈,开销大。Go语言的高并发特性依赖于goruntine和channel,采用channel进行通信可以控制并发的数量,可以使得生产者和消费者解耦,提高代码可读性

2. channel的底层结构

channel的底层结构是一个hchan的结构体
Go语言Channel的底层原理详解_第1张图片
环形缓存:
go语言的channel采用的是环形缓存,环形缓存的内存空间可以复用,减少了GC的压力。channel的环形缓存由hchan的五个字段构成。
Go语言Channel的底层原理详解_第2张图片
qcout是环形缓存中已经保存数据的多少,dataqsize表示的是最多能缓存多少数据,buf是一个指向环形缓存第一个成员的指针,elemsize和elemtype是缓存数据的大小和类型。
两个队列
channel中含有两个队列分别是:接收队列发送队列
Go语言Channel的底层原理详解_第3张图片
close状态值
hchan结构体中close的状态值,1表示channel关闭,0表示channel打开
互斥锁:该互斥锁保证同一个时刻只有一个协程可以操作channel

3.channel的基础用法

func main() {
	// channel的创建
	ch := make(chan int, 2) // 创建一个int类型的含有两个缓存的channel
	ch2 := make(chan int)   // 创建一个int类型的不含有缓存的channel

	// 往channel中添加元素
	i := 10
	ch <- i // 将i加入到ch的管道中,由于存在缓存所以不会阻塞
	// channel中输出元素
	x := <-ch
	fmt.Println(x)
	// 没有缓存的channel在往里面添加元素或者接受元素的时候会发生阻塞
	go func() {
		t := <-ch2 // 会发生阻塞,ch2没有缓存
		fmt.Println("阻塞结束,输出", t)
	}()
	ch2 <- 1 //
}

4.Go的发送数据底层原理

(1)直接发送:在发送数据之前,已经有协程在休眠等待,给channel发送数据不需要放在缓存中,把数据拷贝给一个协程,然后唤醒一个协程。

**(2)放入缓存:**没有协程在等待接收,channel是存在缓存的,将数据放在缓存区中,维护索引。

**(3)休眠等待:**没有等待接收的协程,channel没有缓存区,或者缓存区已经被填满了。协程包装成sudog并且加入到发送队列中,休眠等待。

5.Go的接收数据底层原理

(1)有发送等待的协程,并且没有缓存或者缓存空,从发送等待的协程中接收:先将数据拷贝过来,然后唤醒协程。
(2)有等待的发送的协程,从缓存接收:有等待发送的协程,并且有缓存,从缓存取走数据,将休眠协程的数据放到缓存中,并且唤醒休眠协程。

(3)接收缓存:没有协程在等待发送,从缓存中取走一个数据

(4)阻塞接收:没有协程在等待发送,并且缓存区中没有数据或者没有缓存,则直接阻塞。

6.Go中Channel的应用场景

  1. 解耦生产者和消费者,对协程通信进行解耦,生产者只需要往channel里面发送数据,消费者只需要从channel中获取数据。
  2. 控制并发的数量,可以通过channel来控制并发的规模
  3. 可以做任务的定时任务的超时处理,通常和select一起使用。

你可能感兴趣的:(Go语言底层原理,golang,缓存,后端)