当前位置: 首页 > news >正文

iis做网站之vps4399游戏官网

iis做网站之vps,4399游戏官网,做任务挣钱的网站app,在越南做网站都是什么人前面说了golang的channel#xff0c; 今天我们看看golang select 是怎么实现的。 数据结构 type scase struct {c *hchan // chanelem unsafe.Pointer // 数据 } select 非默认的case 中都是处理channel 的 接受和发送#xff0c;所有scase 结构体中c是用来存储…前面说了golang的channel 今天我们看看golang select 是怎么实现的。 数据结构 type scase struct {c *hchan // chanelem unsafe.Pointer // 数据 } select 非默认的case 中都是处理channel 的 接受和发送所有scase 结构体中c是用来存储select 的case中使用的channel 处理流程 select case 场景 编译器在中间代码生成期间会根据 select 中 case 的不同对控制语句进行优化这一过程都发生在cmd/compile/internal/walk/select.go 中下面会根据不同的场景进行分析代码。 没有case 代码示例 func main() {select {} } 如果是空的select语句程序会被阻塞golang 带有死锁监测机制如果当前写成无法被唤醒则会panic 源码解读 在runtime/select.go中可以看到如果cases为空直接调用gopark函数以waitReasonSelectNoCases的原因挂起当前的协程并且无法被唤醒golang监测到直接panic。 同样我们在walk/select.go的walkSelectCases函数中可以看到如果case为空直接调用runtime.block函数 只有一个case 代码示例 func main() {ch : make(chan int)go func() {ch - 1}()select {case data : -ch:fmt.Println(ch data:, data)} } 如果有输入直接打印ch data : 1 , 没有的话会被检测出all goroutines are asleep - deadlock!和没有case的一样 源码解读 如果一个非default case 将读写转换成 ch - 或 - ch 正常的channel读写 func walkSelectCases(cases []*ir.CommClause) []ir.Node {// optimization: one-case select: single op.if ncas 1 {cas : cases[0] //获取caseir.SetPos(cas)l : cas.Init()if cas.Comm ! nil { // 不是默认n : cas.Comm // 获取case的条件语句l append(l, ir.TakeInit(n)...)switch n.Op() {default:base.Fatalf(select %v, n.Op())case ir.OSEND: // 如果是 send 无须处理// already okcase ir.OSELRECV2:r : n.(*ir.AssignListStmt)// 如果不是 data, ok : - ch 类型处理成- chif ir.IsBlank(r.Lhs[0]) ir.IsBlank(r.Lhs[1]) {n r.Rhs[0]break}// 是的话 op设置成data, ok : - ch形式r.SetOp(ir.OAS2RECV)}l append(l, n)}// 将case 条件后要执行的语句加入带执行的列表l append(l, cas.Body...)// 加入 break类型跳出select-case l append(l, ir.NewBranchStmt(base.Pos, ir.OBREAK, nil))return l}// convert case value arguments to addresses.// this rewrite is used by both the general code and the next optimization.var dflt *ir.CommClausefor _, cas : range cases {ir.SetPos(cas)n : cas.Commif n nil {dflt cascontinue}switch n.Op() {case ir.OSEND:n : n.(*ir.SendStmt)n.Value typecheck.NodAddr(n.Value)n.Value typecheck.Expr(n.Value)case ir.OSELRECV2:n : n.(*ir.AssignListStmt)if !ir.IsBlank(n.Lhs[0]) {n.Lhs[0] typecheck.NodAddr(n.Lhs[0])n.Lhs[0] typecheck.Expr(n.Lhs[0])}}} } 两个case一个default 代码示例 func main() {ch : make(chan int)select {case data : -ch:fmt.Println(ch data:, data)default:fmt.Println(default)} } 如果写入就走- 读取反之走默认 源码解读 如果是两个case其中一个是default非default的会根据send还是recv 调用channel的selectnbsend和 selectnbrecv。这两个方法是非阻塞的 func walkSelectCases(cases []*ir.CommClause) []ir.Node){// optimization: two-case select but one is default: single non-blocking op.if ncas 2 dflt ! nil {cas : cases[0]if cas dflt { // 如果是default 放在 cases[1]cas cases[1]}n : cas.Commir.SetPos(n)r : ir.NewIfStmt(base.Pos, nil, nil, nil)r.SetInit(cas.Init())var cond ir.Nodeswitch n.Op() {default:base.Fatalf(select %v, n.Op())case ir.OSEND:// 调用selectnbsend(c, v)// if selectnbsend(c, v) { body } else { default body }n : n.(*ir.SendStmt)ch : n.Chancond mkcall1(chanfn(selectnbsend, 2, ch.Type()), types.Types[types.TBOOL], r.PtrInit(), ch, n.Value)case ir.OSELRECV2:n : n.(*ir.AssignListStmt)recv : n.Rhs[0].(*ir.UnaryExpr)ch : recv.Xelem : n.Lhs[0]if ir.IsBlank(elem) { //空的话 elem NodNilelem typecheck.NodNil()}cond typecheck.Temp(types.Types[types.TBOOL])// 调用 selectnbrecvfn : chanfn(selectnbrecv, 2, ch.Type())call : mkcall1(fn, fn.Type().Results(), r.PtrInit(), elem, ch)as : ir.NewAssignListStmt(r.Pos(), ir.OAS2, []ir.Node{cond, n.Lhs[1]}, []ir.Node{call})r.PtrInit().Append(typecheck.Stmt(as))}r.Cond typecheck.Expr(cond)r.Body cas.Bodyr.Else append(dflt.Init(), dflt.Body...)return []ir.Node{r, ir.NewBranchStmt(base.Pos, ir.OBREAK, nil)}} } 每次尝试从channel读/写值如果不成功则直接返回不会阻塞。从selectnbsend和selectnbrecv看出最后转换成if-else // compiler implements // // select { // case c - v: // ... foo // default: // ... bar // } // // as // // if selectnbsend(c, v) { // ... foo // } else { // ... bar // } // func selectnbsend(c *hchan, elem unsafe.Pointer) (selected bool) {// block:false// chan将 select准换if-elsereturn chansend(c, elem, false, getcallerpc()) }// compiler implements // // select { // case v, ok -c: // ... foo // default: // ... bar // } // // as // // if selected, ok selectnbrecv(v, c); selected { // ... foo // } else { // ... bar // } // func selectnbrecv(elem unsafe.Pointer, c *hchan) (selected, received bool) {// block:false// chan将 select准换if-elsereturn chanrecv(c, elem, false) } 多个case 代码示例 func main() {ch : make(chan int)go func() {tempArr : []int{1,2,3,4,5,6}for i : range tempArr {ch - i}}()go func() {for {select {case i : -ch:println(first: , i)case i : -ch:println(second, i)}}}()time.Sleep(3 * time.Second)} 可以看到多个case会随机选取一个case执行 源码解读 func walkSelectCases(cases []*ir.CommClause) []ir.Node {ncas : len(cases)sellineno : base.Posif dflt ! nil {ncas--}// 定义casorder为ncas大小的case语句的数组casorder : make([]*ir.CommClause, ncas)// 分别定义nsends为发送channel的case个数nrecvs为接收channel的case个数nsends, nrecvs : 0, 0// 多case编译后待执行的语句列表var init []ir.Node// generate sel-structbase.Pos sellineno// 定义selv为长度为ncas的scase类型的数组// scasetype()函数返回的就是scase结构体包含c和elem两个字段selv : typecheck.Temp(types.NewArray(scasetype(), int64(ncas)))init append(init, typecheck.Stmt(ir.NewAssignStmt(base.Pos, selv, nil)))// No initialization for order; runtime.selectgo is responsible for that.// 定义order为2倍的ncas长度的TUINT16类型的数组// 注意selv和order作为runtime.selectgo()函数的入参前者存放scase列表内存地址后者用来做scase排序使用排序是为了便于挑选出待执行的caseorder : typecheck.Temp(types.NewArray(types.Types[types.TUINT16], 2*int64(ncas)))var pc0, pcs ir.Nodeif base.Flag.Race {pcs typecheck.Temp(types.NewArray(types.Types[types.TUINTPTR], int64(ncas)))pc0 typecheck.Expr(typecheck.NodAddr(ir.NewIndexExpr(base.Pos, pcs, ir.NewInt(0))))} else {pc0 typecheck.NodNil()}// register cases 遍历case生成scase对象放到selv中for _, cas : range cases {ir.SetPos(cas)init append(init, ir.TakeInit(cas)...)n : cas.Commif n nil { // default:continue}var i intvar c, elem ir.Nodeswitch n.Op() { // 根据类型获取chan, elem的值default:base.Fatalf(select %v, n.Op())case ir.OSEND: // 发送chan类型i从0开始递增n : n.(*ir.SendStmt)i nsendsnsendsc n.Chanelem n.Valuecase ir.OSELRECV2: // 接收chani从ncas开始递减n : n.(*ir.AssignListStmt)nrecvsi ncas - nrecvsrecv : n.Rhs[0].(*ir.UnaryExpr)c recv.Xelem n.Lhs[0]}casorder[i] cas// 定义一个函数写入c或elem到selv数组setField : func(f string, val ir.Node) {// 放到selv数组r : ir.NewAssignStmt(base.Pos, ir.NewSelectorExpr(base.Pos, ir.ODOT, ir.NewIndexExpr(base.Pos, selv, ir.NewInt(int64(i))), typecheck.Lookup(f)), val)// 添加到带执行列表init append(init, typecheck.Stmt(r))}c typecheck.ConvNop(c, types.Types[types.TUNSAFEPTR])setField(c, c)if !ir.IsBlank(elem) {elem typecheck.ConvNop(elem, types.Types[types.TUNSAFEPTR])setField(elem, elem)}// TODO(mdempsky): There should be a cleaner way to// handle this.if base.Flag.Race {r : mkcallstmt(selectsetpc, typecheck.NodAddr(ir.NewIndexExpr(base.Pos, pcs, ir.NewInt(int64(i)))))init append(init, r)}}// 如果发送chan和接收chan的个数不等于ncas直接报错if nsendsnrecvs ! ncas {base.Fatalf(walkSelectCases: miscount: %v %v ! %v, nsends, nrecvs, ncas)}// run the select 开始执行select动作base.Pos sellineno// 定义chosen, recvOK作为selectgo()函数的两个返回值// chosen 表示被选中的case的索引recvOK表示对于接收操作是否成功接收chosen : typecheck.Temp(types.Types[types.TINT])recvOK : typecheck.Temp(types.Types[types.TBOOL])r : ir.NewAssignListStmt(base.Pos, ir.OAS2, nil, nil)r.Lhs []ir.Node{chosen, recvOK}// 调用runtime.selectgo()函数作为运行时实际执行多case的select动作的函数fn : typecheck.LookupRuntime(selectgo)var fnInit ir.Nodesr.Rhs []ir.Node{mkcall1(fn, fn.Type().Results(), fnInit, bytePtrToIndex(selv, 0), bytePtrToIndex(order, 0), pc0, ir.NewInt(int64(nsends)), ir.NewInt(int64(nrecvs)), ir.NewBool(dflt nil))}init append(init, fnInit...)init append(init, typecheck.Stmt(r))// selv and order are no longer alive after selectgo.// 执行完selectgo()函数后销毁selv和order数组.init append(init, ir.NewUnaryExpr(base.Pos, ir.OVARKILL, selv))init append(init, ir.NewUnaryExpr(base.Pos, ir.OVARKILL, order))if base.Flag.Race {init append(init, ir.NewUnaryExpr(base.Pos, ir.OVARKILL, pcs))}// dispatch cases //定义一个函数根据chosen确定的case分支生成if语句执行该分支的语句dispatch : func(cond ir.Node, cas *ir.CommClause) {cond typecheck.Expr(cond)cond typecheck.DefaultLit(cond, nil)r : ir.NewIfStmt(base.Pos, cond, nil, nil)if n : cas.Comm; n ! nil n.Op() ir.OSELRECV2 {n : n.(*ir.AssignListStmt)if !ir.IsBlank(n.Lhs[1]) {x : ir.NewAssignStmt(base.Pos, n.Lhs[1], recvOK)r.Body.Append(typecheck.Stmt(x))}}r.Body.Append(cas.Body.Take()...)r.Body.Append(ir.NewBranchStmt(base.Pos, ir.OBREAK, nil))init append(init, r)}// 如果多case中有default分支并且chosen小于0执行该default分支if dflt ! nil {ir.SetPos(dflt)dispatch(ir.NewBinaryExpr(base.Pos, ir.OLT, chosen, ir.NewInt(0)), dflt)}// 如果有chosen选中的case分支即chosen等于i则执行该分支for i, cas : range casorder {ir.SetPos(cas)dispatch(ir.NewBinaryExpr(base.Pos, ir.OEQ, chosen, ir.NewInt(int64(i))), cas)}return init }从上面代码可以看出 1- 初始化过程: 生成scase数组,定义selv 存放scase数组内存地址定义order 来给scase排序 2- 遍历所有的case 将case放到带执行列表不包括default 3- 调用runtime。selectgo并将selv和order作为入参传入selectgo 4- 根据selectgo返回的chosen来生成if语句执行对应的case 解锁加锁 加锁的顺序和解锁的顺序相反。 func sellock(scases []scase, lockorder []uint16) {var c *hchanfor _, o : range lockorder {c0 : scases[o].cif c0 ! c {c c0lock(c.lock)}} }func selunlock(scases []scase, lockorder []uint16) {// 我们必须非常小心在解锁最后一把锁后不要触摸sel因为sel可以在最后一次解锁后立即释放。//考虑以下情况。第一个M调用runtime·park在runtime·selectgo中传递sel。//一旦runtime·park解锁了最后一个锁另一个M会使调用select的G再次可运行//并安排其执行。当G在另一个M上运行时它锁定所有锁并释放sel。现在如果第一个M触摸sel它将访问释放的内存。for i : len(lockorder) - 1; i 0; i-- {c : scases[lockorder[i]].cif i 0 c scases[lockorder[i-1]].c {continue // will unlock it on the next iteration}unlock(c.lock)} } selectgo selectgo 处理逻辑 // cas0指向[ncases]scase类型的数组order0指向[2*ncases]uint16类型的数组其中ncases必须65536。 // 返回值有两个, chosen 和 recvOK分别表示选中的case的序号和对接收操作是否接收成功的布尔值 func selectgo(cas0 *scase, order0 *uint16, pc0 *uintptr, nsends, nrecvs int, block bool) (int, bool) {if debugSelect {print(select: cas0, cas0, \n)}// 执行必要的初始化操作并生成处理case的两种顺序轮询顺序polIorder和加锁顺序lockorder。// 为了将scase分配到栈上这里直接给cas1分配了64KB大小的数组同理 给order1分配了128KB大小的数组// NOTE: In order to maintain a lean stack size, the number of scases// is capped at 65536.cas1 : (*[1 16]scase)(unsafe.Pointer(cas0))order1 : (*[1 17]uint16)(unsafe.Pointer(order0))// ncases个数 发送chan个数 接收chan个数ncases : nsends nrecvs// scases是cas1数组的前ncases个元素scases : cas1[:ncases:ncases]// 顺序列表pollorder是order1的0- ncases个元素pollorder : order1[:ncases:ncases]// 加锁列表lockorder是order1的ncase到 2 ncases 个元素lockorder : order1[ncases:][:ncases:ncases]// NOTE: 编译器初始化的pollorder/lockorder的基础数组不是零。// Even when raceenabled is true, there might be select// statements in packages compiled without -race (e.g.,// ensureSigM in runtime/signal_unix.go).var pcs []uintptrif raceenabled pc0 ! nil {pc1 : (*[1 16]uintptr)(unsafe.Pointer(pc0))pcs pc1[:ncases:ncases]}casePC : func(casi int) uintptr {if pcs nil {return 0}return pcs[casi]}var t0 int64if blockprofilerate 0 {t0 cputicks()}// 生成排列顺序norder : 0for i : range scases {cas : scases[i]// Omit cases without channels from the poll and lock orders.// 处理case中channel为空的情况if cas.c nil {cas.elem nil // 便于GCcontinue}// 通过fastrandn函数引入随机性确定pollorder列表中case的随机顺序索引j : fastrandn(uint32(norder 1))pollorder[norder] pollorder[j]pollorder[j] uint16(i)norder}// 重新生成列表pollorder pollorder[:norder]lockorder lockorder[:norder]// 根据chan地址确定lockorder加锁排序列表的顺序// 简单的堆排序以保证nlogn时间复杂度完成排序for i : range lockorder {j : i// 从轮询顺序开始在同一channel上排序。c : scases[pollorder[i]].cfor j 0 scases[lockorder[(j-1)/2]].c.sortkey() c.sortkey() {k : (j - 1) / 2lockorder[j] lockorder[k]j k}lockorder[j] pollorder[i]}for i : len(lockorder) - 1; i 0; i-- {o : lockorder[i]c : scases[o].clockorder[i] lockorder[0]j : 0for {k : j*2 1if k i {break}if k1 i scases[lockorder[k]].c.sortkey() scases[lockorder[k1]].c.sortkey() {k}if c.sortkey() scases[lockorder[k]].c.sortkey() {lockorder[j] lockorder[k]j kcontinue}break}lockorder[j] o}if debugSelect {for i : 0; i1 len(lockorder); i {if scases[lockorder[i]].c.sortkey() scases[lockorder[i1]].c.sortkey() {print(i, i, x, lockorder[i], y, lockorder[i1], \n)throw(select: broken sort)}}}// 锁定select中涉及的所有channelsellock(scases, lockorder)var (gp *gsg *sudogc *hchank *scasesglist *sudogsgnext *sudogqp unsafe.Pointernextp **sudog)// pass 1 - 查找可以等待处理的channelvar casi intvar cas *scasevar caseSuccess boolvar caseReleaseTime int64 -1var recvOK boolfor _, casei : range pollorder {casi int(casei) // case的索引cas scases[casi] c cas.cif casi nsends { // 处理接收channel的casesg c.sendq.dequeue()if sg ! nil {// 如果当前channel的sendq上有等待的goroutine// 跳到recv代码 并从缓冲区读取数据后将等待goroutine中的数据放入到缓冲区中相同的位置goto recv}if c.qcount 0 {//如果当前channel的缓冲区不为空就会跳到bufrecv标签处从缓冲区获取数据goto bufrecv}if c.closed ! 0 {//如果当前channel已经被关闭就会跳到rclose读取末尾数据和收尾工作goto rclose}} else { // 处理发送channel的caseif raceenabled {racereadpc(c.raceaddr(), casePC(casi), chansendpc)}if c.closed ! 0 {// 如果当前channel已经被关闭就会直接跳到sclose标签panic中止程序goto sclose}sg c.recvq.dequeue()if sg ! nil {// 如果当前channel的recvq上有等待的goroutine就会跳到 send标签向channel发送数据goto send}if c.qcount c.dataqsiz {// 如果当前channel的缓冲区存在空闲位置就会将待发送的数据存入缓冲区goto bufsend}}}if !block { // 如果是非阻塞即包含default分支解锁所有channel并返回selunlock(scases, lockorder)casi -1goto retc}// pass 2 - 将当前goroutine根据需要挂在chan的sendq或recvq上gp getg() // 获取当前的groutineif gp.waiting ! nil {throw(gp.waiting ! nil)}nextp gp.waiting // 正在等待的sudog结构按锁定顺序for _, casei : range lockorder {casi int(casei)cas scases[casi]c cas.csg : acquireSudog()// 获取sudog将当前goroutine绑定到sudog上sg.g gpsg.isSelect true// 在分配elem和在gp.waiting上排队sg之间没有堆栈分割copystack可以找到它。sg.elem cas.elemsg.releasetime 0if t0 ! 0 {sg.releasetime -1}sg.c c// 按锁定顺序构建waiting list 。*nextp sgnextp sg.waitlink// 加入相应等待队列if casi nsends {c.sendq.enqueue(sg)} else {c.recvq.enqueue(sg)}}// 被唤醒后会根据 param 来判断是否是由 close 操作唤醒的所以先置为 nilgp.param nil// Signal to anyone trying to shrink our stack that were about// to park on a channel. The window between when this Gs status// changes and when we set gp.activeStackChans is not safe for// stack shrinking.atomic.Store8(gp.parkingOnChan, 1)// 挂起当前goroutinegopark(selparkcommit, nil, waitReasonSelect, traceEvGoBlockSelect, 1)gp.activeStackChans false// 加锁所有的channelsellock(scases, lockorder)gp.selectDone 0sg (*sudog)(gp.param)// param 存放唤醒 goroutine 的 sudog如果是关闭操作唤醒的那么就为 nilgp.param nil// pass 3 - 当前 Goroutine 被唤醒之后找到满足条件的 Channel 并进行处理//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.// 从不成功的通道中退出队列否则它们会堆积在安静的通道上记录成功的案例如果有的话。我们单独将SudoG按锁定顺序连接起来。casi -1cas nilcaseSuccess false// 当前goroutine 的 waiting 链表按照lockorder顺序存放着case的sudogsglist gp.waiting// 在从 gp.waiting 取消case的sudog链接之前清除所有元素便于GCfor sg1 : gp.waiting; sg1 ! nil; sg1 sg1.waitlink {sg1.isSelect falsesg1.elem nilsg1.c nil}// 清楚当前goroutine的waiting链表因为被sg代表的协程唤醒了gp.waiting nilfor _, casei : range lockorder {k scases[casei]// 如果相等说明goroutine是被当前case的channel收发操作唤醒的if sg sglist {// sg唤醒了当前goroutine, 则当前G已经从sg的队列中出队这里不需要再次出队casi int(casei)cas kcaseSuccess sglist.successif sglist.releasetime 0 {caseReleaseTime sglist.releasetime}} else {// 不是此case唤醒当前goroutine, 将goroutine从case对应的队列(发送或接收)出队c k.cif int(casei) nsends {c.sendq.dequeueSudoG(sglist)} else {c.recvq.dequeueSudoG(sglist)}}// 释放当前case的sudog然后处理下一个case的sudogsgnext sglist.waitlinksglist.waitlink nilreleaseSudog(sglist)sglist sgnext}if cas nil {throw(selectgo: bad wakeup)}c cas.cif debugSelect {print(wait-return: cas0, cas0, c, c, cas, cas, send, casi nsends, \n)}if casi nsends {if !caseSuccess {goto sclose}} else {recvOK caseSuccess}if raceenabled {if casi nsends {raceReadObjectPC(c.elemtype, cas.elem, casePC(casi), chansendpc)} else if cas.elem ! nil {raceWriteObjectPC(c.elemtype, cas.elem, casePC(casi), chanrecvpc)}}if msanenabled {if casi nsends {msanread(cas.elem, c.elemtype.size)} else if cas.elem ! nil {msanwrite(cas.elem, c.elemtype.size)}}if asanenabled {if casi nsends {asanread(cas.elem, c.elemtype.size)} else if cas.elem ! nil {asanwrite(cas.elem, c.elemtype.size)}}selunlock(scases, lockorder)goto retcbufrecv:// 能从buffer获取数据if raceenabled {if cas.elem ! nil {raceWriteObjectPC(c.elemtype, cas.elem, casePC(casi), chanrecvpc)}racenotify(c, c.recvx, nil)}if msanenabled cas.elem ! nil {msanwrite(cas.elem, c.elemtype.size)}if asanenabled cas.elem ! nil {asanwrite(cas.elem, c.elemtype.size)}recvOK trueqp chanbuf(c, c.recvx)if cas.elem ! nil {typedmemmove(c.elemtype, cas.elem, qp)}typedmemclr(c.elemtype, qp)c.recvxif c.recvx c.dataqsiz {c.recvx 0}c.qcount--selunlock(scases, lockorder)goto retcbufsend:// 发送数据到缓存if raceenabled {racenotify(c, c.sendx, nil)raceReadObjectPC(c.elemtype, cas.elem, casePC(casi), chansendpc)}if msanenabled {msanread(cas.elem, c.elemtype.size)}if asanenabled {asanread(cas.elem, c.elemtype.size)}typedmemmove(c.elemtype, chanbuf(c, c.sendx), cas.elem)c.sendxif c.sendx c.dataqsiz {c.sendx 0}c.qcountselunlock(scases, lockorder)goto retcrecv:// 从休眠sendersg接收recv(c, sg, cas.elem, func() { selunlock(scases, lockorder) }, 2)if debugSelect {print(syncrecv: cas0, cas0, c, c, \n)}recvOK truegoto retcrclose:// 读取结束的channelselunlock(scases, lockorder)recvOK falseif cas.elem ! nil {typedmemclr(c.elemtype, cas.elem)}if raceenabled {raceacquire(c.raceaddr())}goto retcsend:// 想休眠的接收房发送数据if raceenabled {raceReadObjectPC(c.elemtype, cas.elem, casePC(casi), chansendpc)}if msanenabled {msanread(cas.elem, c.elemtype.size)}if asanenabled {asanread(cas.elem, c.elemtype.size)}send(c, sg, cas.elem, func() { selunlock(scases, lockorder) }, 2)if debugSelect {print(syncsend: cas0, cas0, c, c, \n)}goto retcretc:if caseReleaseTime 0 {blockevent(caseReleaseTime-t0, 1)}return casi, recvOKsclose:// 向关闭的channel发送数据selunlock(scases, lockorder)panic(plainError(send on closed channel)) }总结 简单总结下select对case处理逻辑 1- 空的case 会被golang监听到无法唤醒的协程会panic 2- 如果只有一个case 根据操作类型转换成 - ch 或 成ch - () 会跳用channel 的 chansend , chanrecv 3- 如果一个default 一个非default 的case非default会走 selectnbsend 和 selectnbrecv 非阻塞的方法最后转换成if-else 语句 4- 多个case 的情况下 cmd/compile/internal/walk/select.go 优化程序中 4.1 对 scase 数组 selv order数组初始化将case放在带执行列表中 4.2 调用selectgo函数根据返回的chosen 结果来生成if语句执行对应的case selectgo 函数 1- 随机生成一个便利case 的 轮询 poollorder, 根据channel 地址生成一个枷锁顺序的lockorder。随机顺序保证公平性加锁顺序能够避免思索 2- 根据pollorder顺序查找cases是否包含立即处理的chan 如果有就处理。没有处理的话创建 sudo 结构将当前的G 加入各case的channel 对应的 接收发送队列等待其他G唤醒 3- 当调度器 唤醒当前的G会按照lockorder 访问所有的case。从中找到需要处理的case进行读写处理同时从所有的case 的发送姐搜队列中移除当前的
http://www.zqtcl.cn/news/395089/

相关文章:

  • 做网站推广的方法有哪些高级服装定制网站
  • 网站的百度地图怎么做的广告设计与制作发展趋势
  • 东莞网站建设兼职平面磨床东莞网站建设
  • 北京购物网站建设佛山外贸网站建设咨询
  • 网站图片在手机上做多大最清晰wordpress 表情拉长
  • 深圳住房宝安和建设局网站做网站的软件是是什么
  • 义乌网站建设方式抖音珠宝代运营
  • 郑州 手机网站制作广州网站优化地址
  • 国外效果图网站2022百度seo优化工具
  • 品牌网站建设 磐石网络官方网站网络科技公司 网站建设
  • 厦门启明星网站建设学校网站模板 中文
  • 高端手机网站平台深圳网上申请个人营业执照
  • 沈阳怎么做网站西亚网站建设科技
  • 做外贸免费的网站有哪些专业简历制作
  • 园林景观设计网站推荐国内wordpress主题
  • 一流的免费网站建设摄影网站源码
  • 深圳高端网站设计公司怎样开发手机网站建设
  • 做网站需要用c语言吗新闻热点
  • 做网站需要交维护费么网站建设详细合同范本
  • 网站运营需要做什么静态网站作品
  • 如何做旅游休闲网站苍南做网站
  • wordpress jp theme关键词排名优化公司成都
  • Soho外贸常用网站wordpress下不了插件吗
  • 企业网站建设小技巧有哪些WordPress网站小程序
  • 公司招聘网站续费申请seo编辑是干什么的
  • 58同城泉州网站建设人工投票平台app
  • dede 网站地图 插件网站引导页flash
  • 聊城做网站的公司渠道网站总体结构
  • 北京比较大的网站建设公司wap网站引导页特效
  • 做关于植物的网站即墨网站设计