Golang容器:Channel

Channel概念

Channel管道 是Go语言中一种强大的通信机制,它使得在并发运行的goroutines之间可以高效地交换数据。通过Channel管道,goroutines能够:

  • 同步执行:Channel管道可以用来协调并发任务的执行顺序,确保它们在正确的时间点上执行。
  • 数据共享:与传统的共享内存模型不同,Channel管道提供了一种通过通信来共享数据的方式,这有助于避免竞态条件和锁的复杂性。
  • 消息队列:Channel管道可以作为消息队列使用,允许goroutines发送和接收消息,实现生产者-消费者模式。
  • 线程同步:Channel管道还可以作为线程同步的工具,用于控制线程间的协作和通信。

Channel使用

创建

msgChan:=make(chan int)
masgChan:=make(chan int,100)

chan分为有缓冲区,无缓冲区

有缓冲区:可以向里面写数据,也可以读数据,只有缓冲区满了写操作才会阻塞,当缓冲空了读操作才会阻塞

无缓冲区:只有读操作就绪才能写入数据,否则都是阻塞着的,实现同步。也就是说,发送的数据需要被读取后,发送才会完成

读写操作

使用“< -”操作符,变量位置不同表示不同操作

//向chan中写数据
msgChan<-i
//读取chan中的shuj
i:=<-MsgChan

操作符“<-”标识了数据的流向,处于箭头指向的一方可以看作数据的流入方;反之,则为数据流出方。当channel为流入方时,代表向channel中写入数据;反之,则代表从channel中读取数据。

channel实现原理

Chanel的数据结构是hchan

type hchan struct {
     qcount   uint       // 当前通道中的元素个数
     dataqsiz uint       // 缓冲区大小
     buf      unsafe.Pointer  // 指向存储通道数据的缓冲区的指针,循环队列
     elemsize uint16     // 元素大小
      closed   uint32     // 标志通道是否已关闭
     elemtype *_type     // 元素类型
     sendx    uint       // 发送元素在环形缓冲区索引,用于跟踪循环队列中的位置。
     recvx    uint       // 接收元素在环形缓冲区索引,
     recvq    waitq      // 接收等待队列
    sendq    waitq      // 发送等待队列
     lock mutex          // 通道的锁
}

buf用来存储,dataqsiz表示缓冲区大小,当一个协程向channel写入数据,如果未有足够的空间吧容纳,会被加入sendq队列中,被阻塞的接收数据的协程会加入recvq队列

type waitq struct {
    first *sudog //队列头部
    last  *sudog //队列尾部
}

写数据的过程

  1. 数据准备:在发送方的协程中准备要发送的数据
  2. 首先检查recvq有没有正在等待读取的协程。如果有,就代表没有缓冲区或者缓冲区中没有数据,那么,就从recvq中拿出第一个协程,数据交由该协程处理,然后激活该协程
  3. 如果recvq没有阻塞的协程,则尝试将数据写入缓冲区
  4. 如果没有缓冲区,或者缓冲区已满,则阻塞该协程,并将协程对象加入sendq中

读取数据的过程

  1. 首先检查sendq是否为空,如果不为空,且没有缓冲区,那么就从sendq中获取第一个协程,并复制其数据,然后激活该协程、
  2. 如果sendq不为空,但有缓冲区,那么就从缓冲区中获取数据
  3. 如果sendq为空。同样要尝试从缓冲区中获取数据,如果不能读取数据,则将自己加入recvq中

整体

Chanel各种操作导致阻塞和协程泄露的场景

写操作

  1. 向nil通道发送数据会被阻塞
  2. 向无缓冲channel写数据,如果读协程没有准备好,会阻塞
  3. 无缓冲channel,必须要有读有写,有数据后必须读出来,否则导致channel阻塞,从而使协程阻塞导致协程泄露
  4. 向有缓冲的chanel写数据,如果缓冲已满,会阻塞

读操作

  1. 从nil通道接收数据会被阻塞
  2. 从无缓冲channel读取数据,如果写协程没有准备好,会被阻塞
  3. 从有缓冲channel读数据,如果缓冲为空会阻塞

向已关闭的Channel发送或者读取数据都会发生错误

协程泄露情况

  1. 协程未关闭channel

    如果一个协程创建Channel但没有正确关闭,并且其他协程尝试从中读取或者写入数据,这都会导致死锁和协程泄露

  2. 协程等待永远不会发送的数据

    如果一个协程正在Channel上等待数据,而发送数据的协程已经结束或者阻塞,这多可能导致等待的协程永远无法继续执行

  3. 错误的channel关闭

    不恰当的时机关闭Channel,例如仍有协程等待数据时关闭Channel

  4. 协程在Channel上无限期阻塞

    如果Channel的发送或接收操作没有适当的超时或退出机制,协程可能无限期阻塞在Channel上

  5. 协程未处理Channel关闭

    在读取Channel时Channel已经关闭,但没有相应地退出循环或逻辑,这可能导致goroutine泄露。

  • 18
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值