go语言-channel

channel 底层原理

1.channel实际上是一个队列,遵守先进先出原则。
2.代码中创建的channel实际上是一个指针,占8个字节,指向堆内存中的chan结构数据, hchan。
3.chan结构的主要部分:
   (1) buf :如果channel是有缓冲的,数据存在buf这个循环数组中。
   (2) sendx :下一个要发送的数据的下标。
   (3) recvx :下一个要接受的数据的下标。
   (4) sendq :待发送的数据队列(双向链表)。
   (5) recvq :待接受的数据队列(双向链表)。
   (6) closed :channel是否关闭标志。
   (7) elemtype :channel中的元素类型。
在这里插入图片描述


channel的类型,模式,状态

2种类型:有缓冲,无缓冲
无缓冲有缓冲
创建方式c := make(chan int)c := make(chan int, 5)
发送如之前有数据没有被接收,发送阻塞只有缓冲区满了,发送才会阻塞
接收没有数据过来的话,接收时阻塞的只有缓冲区空了,接收才会阻塞
3种模式 : 只写, 只读, 可读可写
c1 := make(chan<- int)	// 只写
c2 := make(<-chan int)	// 只读
c3 := make(chan int)		// 可读可写
3种状态 :未初始化,关闭,正常
状态 / 操作未初始化关闭正常
关闭panicpanic正常关闭
发送永远阻塞-死锁panic阻塞或者发送成功
接收永远阻塞-死锁缓冲区有数据就读取,没有就接收该channel类型的零值阻塞或者发送-
  • 多个goroutine监听同一个channel,如果这个channel关闭,所有goroutine都能收到信号。

channel什么情况下会死锁

1.对无缓冲channel只写,不读
func dealLock() {
	ch := make(chan int)
	ch <- 3 // 给非缓冲channel写入数据,但是没有地方读出去,会死锁
}
2.对无缓冲channel写了,但是读在写的后面
func dealLock() {
	ch := make(chan int)
	ch <- 3 // 给非缓冲channel写入数据,会阻塞在这里,后面的读取操作无法进行
	num := <-ch
	fmt.Println(num)
}
3.数据超过channel的缓冲区大小
func dealLock() {
	ch := make(chan int,3)
	ch <- 1
	ch <- 2
	ch <- 3
	ch <- 4		// 这里阻塞住了,因为channel只有3个缓冲区
}
4.读取无缓冲的空channel
func dealLock() {
	ch := make(chan int)
	num := <-ch		// 阻塞
}
5.多个协程相互等待
func dealLock() {
	ch1 := make(chan int)
	ch2 := make(chan int)
	// 子协程等待读取 ch1,然后往 ch2 写数据
	// 主协程等待读取 ch2,然后往 ch1 写数据
	// 相互等待,谁也不先发信息,永远等待下去

	// 子协程
	go func() {
		for {
			select {
			case <-ch1:
				fmt.Println("i am son")
				ch2 <- 666
			}
		}
	}()

	// 主协程
	for {
		for {
			select {
			case <-ch2:
				fmt.Println("i am son")
				ch1 <- 666
			}
		}
	}
}

channel可以干啥?

1.通过channel 控制goroutine执行顺序
var wg sync.WaitGroup

func main() {
	// 执行3个协程,创建3个channel
	wg.Add(3) // 执行几个协程,就加几个计数器
	ch1 := make(chan struct{}, 1)
	ch2 := make(chan struct{}, 1)
	ch3 := make(chan struct{}, 1)
	ch1 <- struct{}{} // 刚开始执行第一个协程,就先给第一个任务channel数据
	// 开始顺序执行
	go printMsg("g1", ch1, ch2) // 第一个协程执行完,再执行第二个
	go printMsg("g2", ch2, ch3)
	go printMsg("g3", ch3, ch1)
	// 阻塞等待计数器完成
	wg.Wait()
}

func printMsg(gName string, inChan chan struct{}, outChan chan struct{}) {
	// 模拟执行了 1 秒的任务
	time.Sleep(1 * time.Second)
	// 监听任务channel
	select {
	case <-inChan:
		fmt.Println("goroutine :", gName) // 这里模拟执行业务
		outChan <- struct{}{}             // 给下一个任务通道数据
	}
	// 当上一个协程执行完毕,计数器减 1
	wg.Done()
}

顺序执行goroutine,打印:

groutine : g1
groutine : g2
groutine : g3
2.通过channel 做队列,进行数据传递 / 实现简单的发布订阅
3.利用阻塞特性,实现类似令牌机制,channel中读取到数据相当于读取到令牌,拿到令牌才可以干活
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值