channel 和 select 配合使用

一下这段代码会打印出0到9吗?

func TestChannel(t *testing.T) {

	c := make(chan int)

	go func() {
		for i := 0; i < 10; i++ {
			select {
			case c <- i:
			default:
			}
		}
		fmt.Println("put end")
	}()

	time.Sleep(time.Second)
	for i := range c {
		fmt.Println(i)
	}
}

答案是不会打印出来。接下来看看channel和select实现,来解答为什么不会打印出来。

channel和select部分源码

channel结构定义

type hchan struct {
	qcount   uint           // total data in the queue
	dataqsiz uint           // size of the circular queue
	buf      unsafe.Pointer // points to an array of dataqsiz elements
	elemsize uint16
	closed   uint32
	elemtype *_type // element type
	sendx    uint   // send index
	recvx    uint   // receive index
	recvq    waitq  // list of recv waiters
	sendq    waitq  // list of send waiters

	// lock protects all fields in hchan, as well as several
	// fields in sudogs blocked on this channel.
	//
	// Do not change another G's status while holding this lock
	// (in particular, do not ready a G), as this can deadlock
	// with stack shrinking.
	lock mutex
}

selectgo源码截取

	// pass 1 - look for something already waiting
	var casi int
	var cas *scase
	var caseSuccess bool
	var caseReleaseTime int64 = -1
	var recvOK bool
	for _, casei := range pollorder {
		casi = int(casei)
		cas = &scases[casi]
		c = cas.c

		if casi >= nsends {
			sg = c.sendq.dequeue()
			if sg != nil {
				goto recv
			}
			if c.qcount > 0 {
				goto bufrecv
			}
			if c.closed != 0 {
				goto rclose
			}
		} else {
			if raceenabled {
				racereadpc(c.raceaddr(), casePC(casi), chansendpc)
			}
			if c.closed != 0 {
				goto sclose
			}
			sg = c.recvq.dequeue()
			if sg != nil {
				goto send
			}
			if c.qcount < c.dataqsiz {
				goto bufsend
			}
		}
	}

// 上面的循环没有找到非阻塞可接受或发送数据的case,且非阻塞(即存在defaul),则跳到retc执行default 代码并退出
	if !block {
		selunlock(scases, lockorder)
		casi = -1
		goto retc
	}

selectgo源码解读

if casi >= nsends{....} 这部分代码表示:当case是revc channel时

  •  如果发送队列不为空,跳转到recv接收消息逻辑
  • 如果channel buffer队列还有空位置,跳转到bufrecv逻辑
  • 如果channel关闭,跳转到rclose逻辑

if casi >= nsends{....}else代码表示:当case是send channel时

  •  channel关闭,跳转到sclose逻辑
  • 接收队列不为空,跳转到send逻辑
  • buffer有空位,跳转到bugsend逻辑

看过channe源码的同学应该知道,sendq用于记录等待发送的goroutine。当我们使用 select default 跳过阻塞时,当前的这个goroutine并没有加入到sendq队列中。所以当我们第二段代码从channel读数据时,并没有任何发送goroutine在队列中。

总结

当select走default逻辑时,并不会把当前goroutine加入到其他case中的channel收发队列中。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值