ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

006-Golang1.17源码分析之select

2022-02-21 19:02:12  阅读:289  来源: 互联网

标签:case Golang1.17 nil gp 源码 006 lockorder scase channel


Golang1.17源码分析之select-006

Golang1.17 学习笔记006

源码地址:runtime/select.go

数据结构:

type scase struct {
	c    *hchan         // chan
	elem unsafe.Pointer // data element
}

核心函数:selectgo()

order0 为一个两倍 cas0 数组长度的 buffer,保存 scase 随机序列 pollorder 和 scase 中 channel 地址序列 lockorder

pollorder:每次selectgo执行都会把scase序列打乱,以达到随机检测case的目的

lockorder:所有case语句中channel序列,以达到去重防止对channel加锁时重复加锁的目的

func selectgo(cas0 *scase, order0 *uint16, pc0 *uintptr, nsends, nrecvs int, block bool) (int, bool) {
    // generate permuted order
    norder := 0
    for i := range scases {
        cas := &scases[i]
		// Omit cases without channels from the poll and lock orders.
		if cas.c == nil {
			cas.elem = nil // allow GC
			continue
		}
		// ....
		i++
    }
    pollorder = pollorder[:norder]
	lockorder = lockorder
	
	// sort the cases by Hchan address to get the locking order.
	// simple heap sort, to guarantee n log n time and constant stack footprint.
	// 一系列堆排序操作
	
	// lock all the channels involved in the select
	sellock(scases, lockorder)
	
	// pass 1 - look for something already waiting
	for _, casei := range pollorder {
	}
	
	if !block {
		selunlock(scases, lockorder)
		casi = -1
		goto retc
	}
	
	// pass 2 - enqueue on all chans
	gp = getg()
	if gp.waiting != nil {
		throw("gp.waiting != nil")
	}
	nextp = &gp.waiting
	for _, casei := range lockorder {
	}
	
	// wait for someone to wake us up
	gp.param = nil
	// Signal to anyone trying to shrink our stack that we're about
	// to park on a channel. The window between when this G's status
	// changes and when we set gp.activeStackChans is not safe for
	// stack shrinking.
	gopark(selparkcommit, nil, waitReasonSelect, traceEvGoBlockSelect, 1)

	sellock(scases, lockorder)
	
	// pass 3 - dequeue from unsuccessful chans otherwise they stack up on quiet channels
	// record the successful case, if any. We singly-linked up the SudoGs in lock order.
	// Clear all elem before unlinking from gp.waiting.
	for sg1 := gp.waiting; sg1 != nil; sg1 = sg1.waitlink {
		sg1.isSelect = false
		sg1.elem = nil
		sg1.c = nil
	}
	gp.waiting = nil
	for _, casei := range lockorder {
	}
}

// 伪代码
func selectgo(cas0 *scase, order0 *uint16, ncases int) (int, bool) {
    //1. 锁定scase语句中所有的channel
    //2. 按照随机顺序检测scase中的channel是否ready
    //   2.1 如果case可读,则读取channel中数据,解锁所有的channel,然后返回(case index, true)
    //   2.2 如果case可写,则将数据写入channel,解锁所有的channel,然后返回(case index, false)
    //   2.3 所有case都未ready,则解锁所有的channel,然后返回(default index, false)
    //3. 所有case都未ready,且没有default语句
    //   3.1 将当前协程加入到所有channel的等待队列
    //   3.2 当将协程转入阻塞,等待被唤醒
    //4. 唤醒后返回channel对应的case index
    //   4.1 如果是读操作,解锁所有的channel,然后返回(case index, true)
    //   4.2 如果是写操作,解锁所有的channel,然后返回(case index, false)
}

总结

  • select语句中除default外,各case执行顺序是随机的

  • select语句中如果没有default语句,则会阻塞等待任一case

参考文献:《Go专家编程》之 select

标签:case,Golang1.17,nil,gp,源码,006,lockorder,scase,channel
来源: https://blog.csdn.net/weixin_51546892/article/details/123042255

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有