从零单排之golang:channel使用及源码详解

在这里插入图片描述

  • channel 的特性:不要通过共享内存来通信,而要通过通信来实现内存共享
1. channel是一个先进先出的队列,go语言提倡使用管道来来通信
2. channel在协程间通信是安全的,因为chan的底层数据结构维护一个互斥锁,在读取或者写入资源前会先获取锁
3. channel分为有缓存和无缓存的管道:有缓存的管道数据写满后阻塞管道,直到有其他的协程读取管道内的数据,无缓存的管道必须有其他的协程先读取,因为无缓存的管道写入一个数据就把程序永远的阻塞住了,这时候就会产生一个死锁,无论有缓冲还是无缓冲读取空管道的数据都会阻塞
4. 根据管道的阻塞性可以实现互斥锁的功能,也可以控制协程的执行顺序
5. 管道从流向又可分为:双向管道和单项管道,我们一般申请的资源都是双向的管道,做参数的传递的时候把双向的管道强转为单项的管道来保证数据的安全性,这样就可以很轻松的实现生产消费者的模式
6. 使用完的管道必须关闭
  • 管道的声明和定义
// 有缓存,len=0 cap=1
ch := make(chan int,1)

// 无缓存
ch := make(chan int)
// 只读的管道
onlyReadChan := make(<-chan int)

// 只写的管道
onlyWriteChan := make(chan <-int)
// 写入数据
ch <- 1

// 读取数据

v := <-ch
v, ok := <-ch
select {
case v, ok = <-ch:
 //  ... foo
default:
 //  ... bar
}

for m := range ch {
    // ...   do something
}
  • chan源码
type hchan struct {
 qcount   uint           // queue 里面有效用户元素,这个字段是在元素出对,入队改变的;可以理解为len(chan)
 dataqsiz uint           // 初始化的时候赋值,之后不再改变,指明数组 buffer 的大小;可以理解为cap(chan)
 buf      unsafe.Pointer // 指明 buffer 数组的地址,初始化赋值,之后不会再改变;
 elemsize uint16  // 指明元素的大小,和 dataqsiz 配合使用就能知道 buffer 内存块的大小了;
 closed   uint32
 elemtype *_type // 元素类型,初始化赋值;
 sendx    uint   // send index
 recvx    uint   // receive index
 recvq    waitq  // 等待 recv 响应的对象列表,抽象成 waiters
 sendq    waitq  // 等待 sedn 响应的对象列表,抽象成 waiters

 // 互斥资源的保护锁,官方特意说明,在持有本互斥锁的时候,绝对不要修改 Goroutine 的状态,不能很有可能在栈扩缩容的时候,出现死锁
 lock mutex
}

在 makechan 我们就看到初始化的时候其实只会初始化四个核心字段:

buf :指明 buffer 地址
elemsize :指明元素大小
elemtype :指明元素类型
dataqsiz :指明数组大小

熟悉这几个属性后结合特性基本可以推导它的底层实现
参考:
https://zhuanlan.zhihu.com/p/297053654
https://zhuanlan.zhihu.com/p/118329711

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值