Channel 底层原理

channel(通道)用于goroutine(协程)之间的通信。它提供了一种在不同协程之间传递数据的机制。channel是一种类型安全的、阻塞的、先进先出(FIFO)的数据结构,确保发送的数据按照发送的顺序接收。Go语言提供通过通信来共享内存,而不是通过共享内存来通信

  

1. 基本数据结构

channel的底层源码和相关实现在src/runtime/chan.go中

hchan是Channel底层数据结构对应的结构体

  对应的字段说明如下:

   qcount : 循环数组中的元素数量,长度
  dataqsiz :循环数组的大小,容量
  buf   :指向底层循环数组的指针(环形缓冲区)
  elemsize :
  closed:是否关闭的标志,0:未关闭,1:已关闭
  elemtype *_type : channel中的元素类型
  sendx: 下一次写的位置
  recvx :下一次读的位置
  recvq:读等待队列
  sendq:等待队列
  lock mutex:互斥锁,保证读写channel时的并发安全问题

2. Channel的创建

2.1Channel语法

2.2Channel的基本用法

2.3创建源码分析

func makechan(t *chantype, size int) *hchan {
    //获取无素类型
    elem := t.elem

     // 元素的大小必须小于64K
     // 编译器已经检查了这一点,但是为了安全起见再次进行检查
    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
    switch {
    case mem == 0:
        // 队列或元素大小为0:只分配hchan的内存
        c = (*hchan)(mallocgc(hchanSize, nil, true))
        // Race detector uses this location for synchronization.
        c.buf = c.raceaddr()
    case elem.ptrdata == 0:
        // 元素不包含指针
        // 一次性为hchan和buf分配连续的内存
        c = (*hchan)(mallocgc(hchanSize+mem, nil, true))
        c.buf = add(unsafe.Pointer(c), hchanSize)
    default:
         // 元素包含指针:分别分配hchan结构体和底层循环数组的内存
        c = new(hchan)
        c.buf = mallocgc(mem, elem, true)
    }
    // 设置hchan中的elemsize、elemtype、dataqsiz、lock等属性
    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")
    }
    return c
}

3.Channel发送数据底层原理

3.1发送的情形

3.2直接发送

3.2.1直接发送的原理

3.2.2直接发送实现

3.2.3直接发送源码分析

源码位置:runtime/chan.go 的channelsend

3.3放入缓存

3.3.1放入缓存原理

3.3.2放入缓存实现
3.3.3源码分析

3.4休眠等待

3.4.1休眠等待原理

3.4.2休眠等待实现
3.4.3源码分析

4.Channel接收数据底层原理

4.1channel接收的情况

4.2同步接收

4.2.1同步接收的原理

4.2.2同步接收的实现

4.2.3同步接收源码分析

源码位置runtime/chan.go

4.3异步接收

4.3.1异步接收的原理

4.3.2异步接收的实现

4.3.3异步接 收源码分析

4.4阻塞接收

4.4.1阻塞接收实现的原理

4.4.2阻塞接收实现

4.4.3阻塞接收源码分析

Go 中的 Channel 是并发编程中非常重要的一种数据结构,它提供了一种通信机制,使得不同的 Goroutine 可以在不使用显式锁的情况下进行数据交换。那么它的底层实现原理是什么呢? Go 中的 Channel底层实现是基于 Goroutine 和同步原语的。每个 Channel 都有一个相关联的等待队列,它用来存储等待在 Channel 上的 Goroutine。当一个 Goroutine 向 Channel 写入数据时,如果当前 Channel 中没有空闲的缓存区,那么这个 Goroutine 就会被阻塞,并被加入到等待队列中。如果有其他 Goroutine 从 Channel 中读取了数据,那么这个等待队列中的 Goroutine 就会被唤醒,继续执行。 类似地,当一个 Goroutine 从 Channel 中读取数据时,如果当前 Channel 中没有数据可读,那么这个 Goroutine 就会被阻塞,并被加入到等待队列中。如果有其他 Goroutine 向 Channel 中写入了数据,那么这个等待队列中的 Goroutine 就会被唤醒,继续执行。 需要注意的是,Go 中的 Channel 是类型安全的。也就是说,一个只能接收字符串的 Channel 是不能接收整数的。这是因为 Channel 内部维护了一个元素类型的信息,它会在运行时进行检查,确保只有符合类型要求的数据才能被写入或读取。 总之,Go 中的 Channel底层实现是基于 Goroutine 和同步原语的,并且是类型安全的。它提供了一种非常方便的并发编程机制,使得不同的 Goroutine 可以在不使用显式锁的情况下进行数据交换。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值